1*22028508SToomas Soome /*-
2*22028508SToomas Soome * Copyright (c) 2007-2014, Juniper Networks, Inc.
3*22028508SToomas Soome * All rights reserved.
4*22028508SToomas Soome *
5*22028508SToomas Soome * Redistribution and use in source and binary forms, with or without
6*22028508SToomas Soome * modification, are permitted provided that the following conditions
7*22028508SToomas Soome * are met:
8*22028508SToomas Soome * 1. Redistributions of source code must retain the above copyright
9*22028508SToomas Soome * notice, this list of conditions and the following disclaimer.
10*22028508SToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
11*22028508SToomas Soome * notice, this list of conditions and the following disclaimer in the
12*22028508SToomas Soome * documentation and/or other materials provided with the distribution.
13*22028508SToomas Soome *
14*22028508SToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*22028508SToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*22028508SToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*22028508SToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*22028508SToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*22028508SToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*22028508SToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*22028508SToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*22028508SToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*22028508SToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*22028508SToomas Soome * SUCH DAMAGE.
25*22028508SToomas Soome */
26*22028508SToomas Soome
27*22028508SToomas Soome #include <sys/cdefs.h>
28*22028508SToomas Soome __FBSDID("$FreeBSD$");
29*22028508SToomas Soome
30*22028508SToomas Soome #include "stand.h"
31*22028508SToomas Soome
32*22028508SToomas Soome #include <sys/stat.h>
33*22028508SToomas Soome #include <sys/stdint.h>
34*22028508SToomas Soome #include <string.h>
35*22028508SToomas Soome #include <zlib.h>
36*22028508SToomas Soome
37*22028508SToomas Soome #ifdef PKGFS_DEBUG
38*22028508SToomas Soome #define DBG(x) printf x
39*22028508SToomas Soome #else
40*22028508SToomas Soome #define DBG(x)
41*22028508SToomas Soome #endif
42*22028508SToomas Soome
43*22028508SToomas Soome static int pkg_open(const char *, struct open_file *);
44*22028508SToomas Soome static int pkg_close(struct open_file *);
45*22028508SToomas Soome static int pkg_read(struct open_file *, void *, size_t, size_t *);
46*22028508SToomas Soome static off_t pkg_seek(struct open_file *, off_t, int);
47*22028508SToomas Soome static int pkg_stat(struct open_file *, struct stat *);
48*22028508SToomas Soome static int pkg_readdir(struct open_file *, struct dirent *);
49*22028508SToomas Soome
50*22028508SToomas Soome struct fs_ops pkgfs_fsops = {
51*22028508SToomas Soome "pkg",
52*22028508SToomas Soome pkg_open,
53*22028508SToomas Soome pkg_close,
54*22028508SToomas Soome pkg_read,
55*22028508SToomas Soome null_write,
56*22028508SToomas Soome pkg_seek,
57*22028508SToomas Soome pkg_stat,
58*22028508SToomas Soome pkg_readdir
59*22028508SToomas Soome };
60*22028508SToomas Soome
61*22028508SToomas Soome #define PKG_BUFSIZE 512
62*22028508SToomas Soome #define PKG_MAXCACHESZ 4096
63*22028508SToomas Soome
64*22028508SToomas Soome #define PKG_FILEEXT ".tgz"
65*22028508SToomas Soome
66*22028508SToomas Soome /*
67*22028508SToomas Soome * Layout of POSIX 'ustar' header.
68*22028508SToomas Soome */
69*22028508SToomas Soome struct ustar_hdr {
70*22028508SToomas Soome char ut_name[100];
71*22028508SToomas Soome char ut_mode[8];
72*22028508SToomas Soome char ut_uid[8];
73*22028508SToomas Soome char ut_gid[8];
74*22028508SToomas Soome char ut_size[12];
75*22028508SToomas Soome char ut_mtime[12];
76*22028508SToomas Soome char ut_checksum[8];
77*22028508SToomas Soome char ut_typeflag[1];
78*22028508SToomas Soome char ut_linkname[100];
79*22028508SToomas Soome char ut_magic[6]; /* For POSIX: "ustar\0" */
80*22028508SToomas Soome char ut_version[2]; /* For POSIX: "00" */
81*22028508SToomas Soome char ut_uname[32];
82*22028508SToomas Soome char ut_gname[32];
83*22028508SToomas Soome char ut_rdevmajor[8];
84*22028508SToomas Soome char ut_rdevminor[8];
85*22028508SToomas Soome union {
86*22028508SToomas Soome struct {
87*22028508SToomas Soome char prefix[155];
88*22028508SToomas Soome } posix;
89*22028508SToomas Soome struct {
90*22028508SToomas Soome char atime[12];
91*22028508SToomas Soome char ctime[12];
92*22028508SToomas Soome char offset[12];
93*22028508SToomas Soome char longnames[4];
94*22028508SToomas Soome char unused[1];
95*22028508SToomas Soome struct gnu_sparse {
96*22028508SToomas Soome char offset[12];
97*22028508SToomas Soome char numbytes[12];
98*22028508SToomas Soome } sparse[4];
99*22028508SToomas Soome char isextended[1];
100*22028508SToomas Soome char realsize[12];
101*22028508SToomas Soome } gnu;
102*22028508SToomas Soome } u;
103*22028508SToomas Soome u_char __padding[12];
104*22028508SToomas Soome };
105*22028508SToomas Soome
106*22028508SToomas Soome struct package;
107*22028508SToomas Soome
108*22028508SToomas Soome struct tarfile
109*22028508SToomas Soome {
110*22028508SToomas Soome struct package *tf_pkg;
111*22028508SToomas Soome struct tarfile *tf_next;
112*22028508SToomas Soome struct ustar_hdr tf_hdr;
113*22028508SToomas Soome off_t tf_ofs;
114*22028508SToomas Soome off_t tf_size;
115*22028508SToomas Soome off_t tf_fp;
116*22028508SToomas Soome size_t tf_cachesz;
117*22028508SToomas Soome void *tf_cache;
118*22028508SToomas Soome };
119*22028508SToomas Soome
120*22028508SToomas Soome struct package
121*22028508SToomas Soome {
122*22028508SToomas Soome struct package *pkg_chain;
123*22028508SToomas Soome int pkg_fd;
124*22028508SToomas Soome off_t pkg_ofs;
125*22028508SToomas Soome z_stream pkg_zs;
126*22028508SToomas Soome struct tarfile *pkg_first;
127*22028508SToomas Soome struct tarfile *pkg_last;
128*22028508SToomas Soome u_char pkg_buf[PKG_BUFSIZE];
129*22028508SToomas Soome };
130*22028508SToomas Soome
131*22028508SToomas Soome static struct package *package = NULL;
132*22028508SToomas Soome
133*22028508SToomas Soome static int new_package(int, struct package **);
134*22028508SToomas Soome
135*22028508SToomas Soome void
pkgfs_cleanup(void)136*22028508SToomas Soome pkgfs_cleanup(void)
137*22028508SToomas Soome {
138*22028508SToomas Soome struct package *chain;
139*22028508SToomas Soome struct tarfile *tf, *tfn;
140*22028508SToomas Soome
141*22028508SToomas Soome while (package != NULL) {
142*22028508SToomas Soome inflateEnd(&package->pkg_zs);
143*22028508SToomas Soome close(package->pkg_fd);
144*22028508SToomas Soome
145*22028508SToomas Soome tf = package->pkg_first;
146*22028508SToomas Soome while (tf != NULL) {
147*22028508SToomas Soome tfn = tf->tf_next;
148*22028508SToomas Soome if (tf->tf_cachesz > 0)
149*22028508SToomas Soome free(tf->tf_cache);
150*22028508SToomas Soome free(tf);
151*22028508SToomas Soome tf = tfn;
152*22028508SToomas Soome }
153*22028508SToomas Soome
154*22028508SToomas Soome chain = package->pkg_chain;
155*22028508SToomas Soome free(package);
156*22028508SToomas Soome package = chain;
157*22028508SToomas Soome }
158*22028508SToomas Soome }
159*22028508SToomas Soome
160*22028508SToomas Soome int
pkgfs_init(const char * pkgname,struct fs_ops * proto)161*22028508SToomas Soome pkgfs_init(const char *pkgname, struct fs_ops *proto)
162*22028508SToomas Soome {
163*22028508SToomas Soome struct package *pkg;
164*22028508SToomas Soome int error, fd;
165*22028508SToomas Soome
166*22028508SToomas Soome if (proto != &pkgfs_fsops)
167*22028508SToomas Soome pkgfs_cleanup();
168*22028508SToomas Soome
169*22028508SToomas Soome exclusive_file_system = proto;
170*22028508SToomas Soome
171*22028508SToomas Soome fd = open(pkgname, O_RDONLY);
172*22028508SToomas Soome
173*22028508SToomas Soome exclusive_file_system = NULL;
174*22028508SToomas Soome
175*22028508SToomas Soome if (fd == -1)
176*22028508SToomas Soome return (errno);
177*22028508SToomas Soome
178*22028508SToomas Soome error = new_package(fd, &pkg);
179*22028508SToomas Soome if (error) {
180*22028508SToomas Soome close(fd);
181*22028508SToomas Soome return (error);
182*22028508SToomas Soome }
183*22028508SToomas Soome
184*22028508SToomas Soome if (pkg == NULL)
185*22028508SToomas Soome return (EDOOFUS);
186*22028508SToomas Soome
187*22028508SToomas Soome pkg->pkg_chain = package;
188*22028508SToomas Soome package = pkg;
189*22028508SToomas Soome exclusive_file_system = &pkgfs_fsops;
190*22028508SToomas Soome return (0);
191*22028508SToomas Soome }
192*22028508SToomas Soome
193*22028508SToomas Soome static int get_mode(struct tarfile *);
194*22028508SToomas Soome static int get_zipped(struct package *, void *, size_t);
195*22028508SToomas Soome static int new_package(int, struct package **);
196*22028508SToomas Soome static struct tarfile *scan_tarfile(struct package *, struct tarfile *);
197*22028508SToomas Soome
198*22028508SToomas Soome static int
pkg_open(const char * fn,struct open_file * f)199*22028508SToomas Soome pkg_open(const char *fn, struct open_file *f)
200*22028508SToomas Soome {
201*22028508SToomas Soome struct tarfile *tf;
202*22028508SToomas Soome
203*22028508SToomas Soome if (fn == NULL || f == NULL)
204*22028508SToomas Soome return (EINVAL);
205*22028508SToomas Soome
206*22028508SToomas Soome if (package == NULL)
207*22028508SToomas Soome return (ENXIO);
208*22028508SToomas Soome
209*22028508SToomas Soome /*
210*22028508SToomas Soome * We can only read from a package, so reject request to open
211*22028508SToomas Soome * for write-only or read-write.
212*22028508SToomas Soome */
213*22028508SToomas Soome if (f->f_flags != F_READ)
214*22028508SToomas Soome return (EPERM);
215*22028508SToomas Soome
216*22028508SToomas Soome /*
217*22028508SToomas Soome * Scan the file headers for the named file. We stop scanning
218*22028508SToomas Soome * at the first filename that has the .pkg extension. This is
219*22028508SToomas Soome * a package within a package. We assume we have all the files
220*22028508SToomas Soome * we need up-front and without having to dig within nested
221*22028508SToomas Soome * packages.
222*22028508SToomas Soome *
223*22028508SToomas Soome * Note that we preserve streaming properties as much as possible.
224*22028508SToomas Soome */
225*22028508SToomas Soome while (*fn == '/')
226*22028508SToomas Soome fn++;
227*22028508SToomas Soome
228*22028508SToomas Soome /*
229*22028508SToomas Soome * Allow opening of the root directory for use by readdir()
230*22028508SToomas Soome * to support listing files in the package.
231*22028508SToomas Soome */
232*22028508SToomas Soome if (*fn == '\0') {
233*22028508SToomas Soome f->f_fsdata = NULL;
234*22028508SToomas Soome return (0);
235*22028508SToomas Soome }
236*22028508SToomas Soome
237*22028508SToomas Soome tf = scan_tarfile(package, NULL);
238*22028508SToomas Soome while (tf != NULL) {
239*22028508SToomas Soome if (strcmp(fn, tf->tf_hdr.ut_name) == 0) {
240*22028508SToomas Soome f->f_fsdata = tf;
241*22028508SToomas Soome tf->tf_fp = 0; /* Reset the file pointer. */
242*22028508SToomas Soome return (0);
243*22028508SToomas Soome }
244*22028508SToomas Soome tf = scan_tarfile(package, tf);
245*22028508SToomas Soome }
246*22028508SToomas Soome return (errno);
247*22028508SToomas Soome }
248*22028508SToomas Soome
249*22028508SToomas Soome static int
pkg_close(struct open_file * f)250*22028508SToomas Soome pkg_close(struct open_file *f)
251*22028508SToomas Soome {
252*22028508SToomas Soome struct tarfile *tf;
253*22028508SToomas Soome
254*22028508SToomas Soome tf = (struct tarfile *)f->f_fsdata;
255*22028508SToomas Soome if (tf == NULL)
256*22028508SToomas Soome return (0);
257*22028508SToomas Soome
258*22028508SToomas Soome /*
259*22028508SToomas Soome * Free up the cache if we read all of the file.
260*22028508SToomas Soome */
261*22028508SToomas Soome if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) {
262*22028508SToomas Soome free(tf->tf_cache);
263*22028508SToomas Soome tf->tf_cachesz = 0;
264*22028508SToomas Soome }
265*22028508SToomas Soome return (0);
266*22028508SToomas Soome }
267*22028508SToomas Soome
268*22028508SToomas Soome static int
pkg_read(struct open_file * f,void * buf,size_t size,size_t * res)269*22028508SToomas Soome pkg_read(struct open_file *f, void *buf, size_t size, size_t *res)
270*22028508SToomas Soome {
271*22028508SToomas Soome struct tarfile *tf;
272*22028508SToomas Soome char *p;
273*22028508SToomas Soome off_t fp;
274*22028508SToomas Soome size_t sz;
275*22028508SToomas Soome
276*22028508SToomas Soome tf = (struct tarfile *)f->f_fsdata;
277*22028508SToomas Soome if (tf == NULL) {
278*22028508SToomas Soome if (res != NULL)
279*22028508SToomas Soome *res = size;
280*22028508SToomas Soome return (EBADF);
281*22028508SToomas Soome }
282*22028508SToomas Soome
283*22028508SToomas Soome fp = tf->tf_fp;
284*22028508SToomas Soome p = buf;
285*22028508SToomas Soome sz = 0;
286*22028508SToomas Soome while (size > 0) {
287*22028508SToomas Soome sz = tf->tf_size - fp;
288*22028508SToomas Soome if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size)
289*22028508SToomas Soome sz = tf->tf_cachesz - fp;
290*22028508SToomas Soome if (size < sz)
291*22028508SToomas Soome sz = size;
292*22028508SToomas Soome if (sz == 0)
293*22028508SToomas Soome break;
294*22028508SToomas Soome
295*22028508SToomas Soome if (fp < tf->tf_cachesz) {
296*22028508SToomas Soome /* Satisfy the request from cache. */
297*22028508SToomas Soome memcpy(p, tf->tf_cache + fp, sz);
298*22028508SToomas Soome fp += sz;
299*22028508SToomas Soome p += sz;
300*22028508SToomas Soome size -= sz;
301*22028508SToomas Soome continue;
302*22028508SToomas Soome }
303*22028508SToomas Soome
304*22028508SToomas Soome if (get_zipped(tf->tf_pkg, p, sz) == -1) {
305*22028508SToomas Soome sz = -1;
306*22028508SToomas Soome break;
307*22028508SToomas Soome }
308*22028508SToomas Soome
309*22028508SToomas Soome fp += sz;
310*22028508SToomas Soome p += sz;
311*22028508SToomas Soome size -= sz;
312*22028508SToomas Soome
313*22028508SToomas Soome if (tf->tf_cachesz != 0)
314*22028508SToomas Soome continue;
315*22028508SToomas Soome
316*22028508SToomas Soome tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ;
317*22028508SToomas Soome tf->tf_cache = malloc(tf->tf_cachesz);
318*22028508SToomas Soome if (tf->tf_cache != NULL)
319*22028508SToomas Soome memcpy(tf->tf_cache, buf, tf->tf_cachesz);
320*22028508SToomas Soome else
321*22028508SToomas Soome tf->tf_cachesz = 0;
322*22028508SToomas Soome }
323*22028508SToomas Soome
324*22028508SToomas Soome tf->tf_fp = fp;
325*22028508SToomas Soome if (res != NULL)
326*22028508SToomas Soome *res = size;
327*22028508SToomas Soome return ((sz == -1) ? errno : 0);
328*22028508SToomas Soome }
329*22028508SToomas Soome
330*22028508SToomas Soome static off_t
pkg_seek(struct open_file * f,off_t ofs,int whence)331*22028508SToomas Soome pkg_seek(struct open_file *f, off_t ofs, int whence)
332*22028508SToomas Soome {
333*22028508SToomas Soome char buf[512];
334*22028508SToomas Soome struct tarfile *tf;
335*22028508SToomas Soome off_t delta;
336*22028508SToomas Soome size_t sz, res;
337*22028508SToomas Soome int error;
338*22028508SToomas Soome
339*22028508SToomas Soome tf = (struct tarfile *)f->f_fsdata;
340*22028508SToomas Soome if (tf == NULL) {
341*22028508SToomas Soome errno = EBADF;
342*22028508SToomas Soome return (-1);
343*22028508SToomas Soome }
344*22028508SToomas Soome
345*22028508SToomas Soome switch (whence) {
346*22028508SToomas Soome case SEEK_SET:
347*22028508SToomas Soome delta = ofs - tf->tf_fp;
348*22028508SToomas Soome break;
349*22028508SToomas Soome case SEEK_CUR:
350*22028508SToomas Soome delta = ofs;
351*22028508SToomas Soome break;
352*22028508SToomas Soome case SEEK_END:
353*22028508SToomas Soome delta = tf->tf_size - tf->tf_fp + ofs;
354*22028508SToomas Soome break;
355*22028508SToomas Soome default:
356*22028508SToomas Soome errno = EINVAL;
357*22028508SToomas Soome return (-1);
358*22028508SToomas Soome }
359*22028508SToomas Soome
360*22028508SToomas Soome if (delta < 0) {
361*22028508SToomas Soome DBG(("%s: negative file seek (%jd)\n", __func__,
362*22028508SToomas Soome (intmax_t)delta));
363*22028508SToomas Soome errno = ESPIPE;
364*22028508SToomas Soome return (-1);
365*22028508SToomas Soome }
366*22028508SToomas Soome
367*22028508SToomas Soome while (delta > 0 && tf->tf_fp < tf->tf_size) {
368*22028508SToomas Soome sz = (delta > sizeof(buf)) ? sizeof(buf) : delta;
369*22028508SToomas Soome error = pkg_read(f, buf, sz, &res);
370*22028508SToomas Soome if (error != 0) {
371*22028508SToomas Soome errno = error;
372*22028508SToomas Soome return (-1);
373*22028508SToomas Soome }
374*22028508SToomas Soome delta -= sz - res;
375*22028508SToomas Soome }
376*22028508SToomas Soome
377*22028508SToomas Soome return (tf->tf_fp);
378*22028508SToomas Soome }
379*22028508SToomas Soome
380*22028508SToomas Soome static int
pkg_stat(struct open_file * f,struct stat * sb)381*22028508SToomas Soome pkg_stat(struct open_file *f, struct stat *sb)
382*22028508SToomas Soome {
383*22028508SToomas Soome struct tarfile *tf;
384*22028508SToomas Soome
385*22028508SToomas Soome tf = (struct tarfile *)f->f_fsdata;
386*22028508SToomas Soome if (tf == NULL)
387*22028508SToomas Soome return (EBADF);
388*22028508SToomas Soome memset(sb, 0, sizeof(*sb));
389*22028508SToomas Soome sb->st_mode = get_mode(tf);
390*22028508SToomas Soome sb->st_size = tf->tf_size;
391*22028508SToomas Soome sb->st_blocks = (tf->tf_size + 511) / 512;
392*22028508SToomas Soome return (0);
393*22028508SToomas Soome }
394*22028508SToomas Soome
395*22028508SToomas Soome static int
pkg_readdir(struct open_file * f,struct dirent * d)396*22028508SToomas Soome pkg_readdir(struct open_file *f, struct dirent *d)
397*22028508SToomas Soome {
398*22028508SToomas Soome struct tarfile *tf;
399*22028508SToomas Soome
400*22028508SToomas Soome tf = (struct tarfile *)f->f_fsdata;
401*22028508SToomas Soome if (tf != NULL)
402*22028508SToomas Soome return (EBADF);
403*22028508SToomas Soome
404*22028508SToomas Soome tf = scan_tarfile(package, NULL);
405*22028508SToomas Soome if (tf == NULL)
406*22028508SToomas Soome return (ENOENT);
407*22028508SToomas Soome
408*22028508SToomas Soome d->d_fileno = 0;
409*22028508SToomas Soome d->d_reclen = sizeof(*d);
410*22028508SToomas Soome d->d_type = DT_REG;
411*22028508SToomas Soome memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name));
412*22028508SToomas Soome return (0);
413*22028508SToomas Soome }
414*22028508SToomas Soome
415*22028508SToomas Soome /*
416*22028508SToomas Soome * Low-level support functions.
417*22028508SToomas Soome */
418*22028508SToomas Soome
419*22028508SToomas Soome static int
get_byte(struct package * pkg,off_t * op)420*22028508SToomas Soome get_byte(struct package *pkg, off_t *op)
421*22028508SToomas Soome {
422*22028508SToomas Soome int c;
423*22028508SToomas Soome
424*22028508SToomas Soome if (pkg->pkg_zs.avail_in == 0) {
425*22028508SToomas Soome c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
426*22028508SToomas Soome if (c <= 0)
427*22028508SToomas Soome return (-1);
428*22028508SToomas Soome pkg->pkg_zs.avail_in = c;
429*22028508SToomas Soome pkg->pkg_zs.next_in = pkg->pkg_buf;
430*22028508SToomas Soome }
431*22028508SToomas Soome
432*22028508SToomas Soome c = *pkg->pkg_zs.next_in;
433*22028508SToomas Soome pkg->pkg_zs.next_in++;
434*22028508SToomas Soome pkg->pkg_zs.avail_in--;
435*22028508SToomas Soome (*op)++;
436*22028508SToomas Soome return (c);
437*22028508SToomas Soome }
438*22028508SToomas Soome
439*22028508SToomas Soome static int
get_zipped(struct package * pkg,void * buf,size_t bufsz)440*22028508SToomas Soome get_zipped(struct package *pkg, void *buf, size_t bufsz)
441*22028508SToomas Soome {
442*22028508SToomas Soome int c;
443*22028508SToomas Soome
444*22028508SToomas Soome pkg->pkg_zs.next_out = buf;
445*22028508SToomas Soome pkg->pkg_zs.avail_out = bufsz;
446*22028508SToomas Soome
447*22028508SToomas Soome while (pkg->pkg_zs.avail_out) {
448*22028508SToomas Soome if (pkg->pkg_zs.avail_in == 0) {
449*22028508SToomas Soome c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
450*22028508SToomas Soome if (c <= 0) {
451*22028508SToomas Soome errno = EIO;
452*22028508SToomas Soome return (-1);
453*22028508SToomas Soome }
454*22028508SToomas Soome pkg->pkg_zs.avail_in = c;
455*22028508SToomas Soome pkg->pkg_zs.next_in = pkg->pkg_buf;
456*22028508SToomas Soome }
457*22028508SToomas Soome
458*22028508SToomas Soome c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH);
459*22028508SToomas Soome if (c != Z_OK && c != Z_STREAM_END) {
460*22028508SToomas Soome errno = EIO;
461*22028508SToomas Soome return (-1);
462*22028508SToomas Soome }
463*22028508SToomas Soome }
464*22028508SToomas Soome
465*22028508SToomas Soome pkg->pkg_ofs += bufsz;
466*22028508SToomas Soome return (0);
467*22028508SToomas Soome }
468*22028508SToomas Soome
469*22028508SToomas Soome static int
cache_data(struct tarfile * tf)470*22028508SToomas Soome cache_data(struct tarfile *tf)
471*22028508SToomas Soome {
472*22028508SToomas Soome struct package *pkg;
473*22028508SToomas Soome size_t sz;
474*22028508SToomas Soome
475*22028508SToomas Soome if (tf == NULL) {
476*22028508SToomas Soome DBG(("%s: no file to cache data for?\n", __func__));
477*22028508SToomas Soome errno = EINVAL;
478*22028508SToomas Soome return (-1);
479*22028508SToomas Soome }
480*22028508SToomas Soome
481*22028508SToomas Soome pkg = tf->tf_pkg;
482*22028508SToomas Soome if (pkg == NULL) {
483*22028508SToomas Soome DBG(("%s: no package associated with file?\n", __func__));
484*22028508SToomas Soome errno = EINVAL;
485*22028508SToomas Soome return (-1);
486*22028508SToomas Soome }
487*22028508SToomas Soome
488*22028508SToomas Soome if (tf->tf_ofs != pkg->pkg_ofs) {
489*22028508SToomas Soome DBG(("%s: caching after partial read of file %s?\n",
490*22028508SToomas Soome __func__, tf->tf_hdr.ut_name));
491*22028508SToomas Soome errno = EINVAL;
492*22028508SToomas Soome return (-1);
493*22028508SToomas Soome }
494*22028508SToomas Soome
495*22028508SToomas Soome /* We don't cache everything... */
496*22028508SToomas Soome if (tf->tf_size > PKG_MAXCACHESZ) {
497*22028508SToomas Soome errno = ENOMEM;
498*22028508SToomas Soome return (-1);
499*22028508SToomas Soome }
500*22028508SToomas Soome
501*22028508SToomas Soome /* All files are padded to a multiple of 512 bytes. */
502*22028508SToomas Soome sz = (tf->tf_size + 0x1ff) & ~0x1ff;
503*22028508SToomas Soome
504*22028508SToomas Soome tf->tf_cache = malloc(sz);
505*22028508SToomas Soome if (tf->tf_cache == NULL) {
506*22028508SToomas Soome DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz));
507*22028508SToomas Soome errno = ENOMEM;
508*22028508SToomas Soome return (-1);
509*22028508SToomas Soome }
510*22028508SToomas Soome
511*22028508SToomas Soome tf->tf_cachesz = sz;
512*22028508SToomas Soome return (get_zipped(pkg, tf->tf_cache, sz));
513*22028508SToomas Soome }
514*22028508SToomas Soome
515*22028508SToomas Soome /*
516*22028508SToomas Soome * Note that this implementation does not (and should not!) obey
517*22028508SToomas Soome * locale settings; you cannot simply substitute strtol here, since
518*22028508SToomas Soome * it does obey locale.
519*22028508SToomas Soome */
520*22028508SToomas Soome static off_t
pkg_atol8(const char * p,unsigned char_cnt)521*22028508SToomas Soome pkg_atol8(const char *p, unsigned char_cnt)
522*22028508SToomas Soome {
523*22028508SToomas Soome int64_t l, limit, last_digit_limit;
524*22028508SToomas Soome int digit, sign, base;
525*22028508SToomas Soome
526*22028508SToomas Soome base = 8;
527*22028508SToomas Soome limit = INT64_MAX / base;
528*22028508SToomas Soome last_digit_limit = INT64_MAX % base;
529*22028508SToomas Soome
530*22028508SToomas Soome while (*p == ' ' || *p == '\t')
531*22028508SToomas Soome p++;
532*22028508SToomas Soome if (*p == '-') {
533*22028508SToomas Soome sign = -1;
534*22028508SToomas Soome p++;
535*22028508SToomas Soome } else
536*22028508SToomas Soome sign = 1;
537*22028508SToomas Soome
538*22028508SToomas Soome l = 0;
539*22028508SToomas Soome digit = *p - '0';
540*22028508SToomas Soome while (digit >= 0 && digit < base && char_cnt-- > 0) {
541*22028508SToomas Soome if (l>limit || (l == limit && digit > last_digit_limit)) {
542*22028508SToomas Soome l = UINT64_MAX; /* Truncate on overflow. */
543*22028508SToomas Soome break;
544*22028508SToomas Soome }
545*22028508SToomas Soome l = (l * base) + digit;
546*22028508SToomas Soome digit = *++p - '0';
547*22028508SToomas Soome }
548*22028508SToomas Soome return (sign < 0) ? -l : l;
549*22028508SToomas Soome }
550*22028508SToomas Soome
551*22028508SToomas Soome /*
552*22028508SToomas Soome * Parse a base-256 integer. This is just a straight signed binary
553*22028508SToomas Soome * value in big-endian order, except that the high-order bit is
554*22028508SToomas Soome * ignored. Remember that "int64_t" may or may not be exactly 64
555*22028508SToomas Soome * bits; the implementation here tries to avoid making any assumptions
556*22028508SToomas Soome * about the actual size of an int64_t. It does assume we're using
557*22028508SToomas Soome * twos-complement arithmetic, though.
558*22028508SToomas Soome */
559*22028508SToomas Soome static int64_t
pkg_atol256(const char * _p,unsigned char_cnt)560*22028508SToomas Soome pkg_atol256(const char *_p, unsigned char_cnt)
561*22028508SToomas Soome {
562*22028508SToomas Soome int64_t l, upper_limit, lower_limit;
563*22028508SToomas Soome const unsigned char *p = (const unsigned char *)_p;
564*22028508SToomas Soome
565*22028508SToomas Soome upper_limit = INT64_MAX / 256;
566*22028508SToomas Soome lower_limit = INT64_MIN / 256;
567*22028508SToomas Soome
568*22028508SToomas Soome /* Pad with 1 or 0 bits, depending on sign. */
569*22028508SToomas Soome if ((0x40 & *p) == 0x40)
570*22028508SToomas Soome l = (int64_t)-1;
571*22028508SToomas Soome else
572*22028508SToomas Soome l = 0;
573*22028508SToomas Soome l = (l << 6) | (0x3f & *p++);
574*22028508SToomas Soome while (--char_cnt > 0) {
575*22028508SToomas Soome if (l > upper_limit) {
576*22028508SToomas Soome l = INT64_MAX; /* Truncate on overflow */
577*22028508SToomas Soome break;
578*22028508SToomas Soome } else if (l < lower_limit) {
579*22028508SToomas Soome l = INT64_MIN;
580*22028508SToomas Soome break;
581*22028508SToomas Soome }
582*22028508SToomas Soome l = (l << 8) | (0xff & (int64_t)*p++);
583*22028508SToomas Soome }
584*22028508SToomas Soome return (l);
585*22028508SToomas Soome }
586*22028508SToomas Soome
587*22028508SToomas Soome static off_t
pkg_atol(const char * p,unsigned char_cnt)588*22028508SToomas Soome pkg_atol(const char *p, unsigned char_cnt)
589*22028508SToomas Soome {
590*22028508SToomas Soome /*
591*22028508SToomas Soome * Technically, GNU pkg considers a field to be in base-256
592*22028508SToomas Soome * only if the first byte is 0xff or 0x80.
593*22028508SToomas Soome */
594*22028508SToomas Soome if (*p & 0x80)
595*22028508SToomas Soome return (pkg_atol256(p, char_cnt));
596*22028508SToomas Soome return (pkg_atol8(p, char_cnt));
597*22028508SToomas Soome }
598*22028508SToomas Soome
599*22028508SToomas Soome static int
get_mode(struct tarfile * tf)600*22028508SToomas Soome get_mode(struct tarfile *tf)
601*22028508SToomas Soome {
602*22028508SToomas Soome return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode)));
603*22028508SToomas Soome }
604*22028508SToomas Soome
605*22028508SToomas Soome /* GZip flag byte */
606*22028508SToomas Soome #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
607*22028508SToomas Soome #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
608*22028508SToomas Soome #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
609*22028508SToomas Soome #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
610*22028508SToomas Soome #define COMMENT 0x10 /* bit 4 set: file comment present */
611*22028508SToomas Soome #define RESERVED 0xE0 /* bits 5..7: reserved */
612*22028508SToomas Soome
613*22028508SToomas Soome static int
new_package(int fd,struct package ** pp)614*22028508SToomas Soome new_package(int fd, struct package **pp)
615*22028508SToomas Soome {
616*22028508SToomas Soome struct package *pkg;
617*22028508SToomas Soome off_t ofs;
618*22028508SToomas Soome int flags, i, error;
619*22028508SToomas Soome
620*22028508SToomas Soome pkg = malloc(sizeof(*pkg));
621*22028508SToomas Soome if (pkg == NULL)
622*22028508SToomas Soome return (ENOMEM);
623*22028508SToomas Soome
624*22028508SToomas Soome bzero(pkg, sizeof(*pkg));
625*22028508SToomas Soome pkg->pkg_fd = fd;
626*22028508SToomas Soome
627*22028508SToomas Soome /*
628*22028508SToomas Soome * Parse the header.
629*22028508SToomas Soome */
630*22028508SToomas Soome error = EFTYPE;
631*22028508SToomas Soome ofs = 0;
632*22028508SToomas Soome
633*22028508SToomas Soome /* Check megic. */
634*22028508SToomas Soome if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b)
635*22028508SToomas Soome goto fail;
636*22028508SToomas Soome /* Check method. */
637*22028508SToomas Soome if (get_byte(pkg, &ofs) != Z_DEFLATED)
638*22028508SToomas Soome goto fail;
639*22028508SToomas Soome /* Check flags. */
640*22028508SToomas Soome flags = get_byte(pkg, &ofs);
641*22028508SToomas Soome if (flags & RESERVED)
642*22028508SToomas Soome goto fail;
643*22028508SToomas Soome
644*22028508SToomas Soome /* Skip time, xflags and OS code. */
645*22028508SToomas Soome for (i = 0; i < 6; i++) {
646*22028508SToomas Soome if (get_byte(pkg, &ofs) == -1)
647*22028508SToomas Soome goto fail;
648*22028508SToomas Soome }
649*22028508SToomas Soome
650*22028508SToomas Soome /* Skip extra field. */
651*22028508SToomas Soome if (flags & EXTRA_FIELD) {
652*22028508SToomas Soome i = (get_byte(pkg, &ofs) & 0xff) |
653*22028508SToomas Soome ((get_byte(pkg, &ofs) << 8) & 0xff);
654*22028508SToomas Soome while (i-- > 0) {
655*22028508SToomas Soome if (get_byte(pkg, &ofs) == -1)
656*22028508SToomas Soome goto fail;
657*22028508SToomas Soome }
658*22028508SToomas Soome }
659*22028508SToomas Soome
660*22028508SToomas Soome /* Skip original file name. */
661*22028508SToomas Soome if (flags & ORIG_NAME) {
662*22028508SToomas Soome do {
663*22028508SToomas Soome i = get_byte(pkg, &ofs);
664*22028508SToomas Soome } while (i != 0 && i != -1);
665*22028508SToomas Soome if (i == -1)
666*22028508SToomas Soome goto fail;
667*22028508SToomas Soome }
668*22028508SToomas Soome
669*22028508SToomas Soome /* Print the comment if it's there. */
670*22028508SToomas Soome if (flags & COMMENT) {
671*22028508SToomas Soome while (1) {
672*22028508SToomas Soome i = get_byte(pkg, &ofs);
673*22028508SToomas Soome if (i == -1)
674*22028508SToomas Soome goto fail;
675*22028508SToomas Soome if (i == 0)
676*22028508SToomas Soome break;
677*22028508SToomas Soome putchar(i);
678*22028508SToomas Soome }
679*22028508SToomas Soome }
680*22028508SToomas Soome
681*22028508SToomas Soome /* Skip the CRC. */
682*22028508SToomas Soome if (flags & HEAD_CRC) {
683*22028508SToomas Soome if (get_byte(pkg, &ofs) == -1)
684*22028508SToomas Soome goto fail;
685*22028508SToomas Soome if (get_byte(pkg, &ofs) == -1)
686*22028508SToomas Soome goto fail;
687*22028508SToomas Soome }
688*22028508SToomas Soome
689*22028508SToomas Soome /*
690*22028508SToomas Soome * Done parsing the ZIP header. Spkgt the inflation engine.
691*22028508SToomas Soome */
692*22028508SToomas Soome error = inflateInit2(&pkg->pkg_zs, -15);
693*22028508SToomas Soome if (error != Z_OK)
694*22028508SToomas Soome goto fail;
695*22028508SToomas Soome
696*22028508SToomas Soome *pp = pkg;
697*22028508SToomas Soome return (0);
698*22028508SToomas Soome
699*22028508SToomas Soome fail:
700*22028508SToomas Soome free(pkg);
701*22028508SToomas Soome return (error);
702*22028508SToomas Soome }
703*22028508SToomas Soome
704*22028508SToomas Soome static struct tarfile *
scan_tarfile(struct package * pkg,struct tarfile * last)705*22028508SToomas Soome scan_tarfile(struct package *pkg, struct tarfile *last)
706*22028508SToomas Soome {
707*22028508SToomas Soome char buf[512];
708*22028508SToomas Soome struct tarfile *cur;
709*22028508SToomas Soome off_t ofs;
710*22028508SToomas Soome size_t sz;
711*22028508SToomas Soome
712*22028508SToomas Soome cur = (last != NULL) ? last->tf_next : pkg->pkg_first;
713*22028508SToomas Soome if (cur == NULL) {
714*22028508SToomas Soome ofs = (last != NULL) ? last->tf_ofs + last->tf_size :
715*22028508SToomas Soome pkg->pkg_ofs;
716*22028508SToomas Soome ofs = (ofs + 0x1ff) & ~0x1ff;
717*22028508SToomas Soome
718*22028508SToomas Soome /* Check if we've reached EOF. */
719*22028508SToomas Soome if (ofs < pkg->pkg_ofs) {
720*22028508SToomas Soome errno = ENOSPC;
721*22028508SToomas Soome return (NULL);
722*22028508SToomas Soome }
723*22028508SToomas Soome
724*22028508SToomas Soome if (ofs != pkg->pkg_ofs) {
725*22028508SToomas Soome if (last != NULL && pkg->pkg_ofs == last->tf_ofs) {
726*22028508SToomas Soome if (cache_data(last) == -1)
727*22028508SToomas Soome return (NULL);
728*22028508SToomas Soome } else {
729*22028508SToomas Soome sz = ofs - pkg->pkg_ofs;
730*22028508SToomas Soome while (sz != 0) {
731*22028508SToomas Soome if (sz > sizeof(buf))
732*22028508SToomas Soome sz = sizeof(buf);
733*22028508SToomas Soome if (get_zipped(pkg, buf, sz) == -1)
734*22028508SToomas Soome return (NULL);
735*22028508SToomas Soome sz = ofs - pkg->pkg_ofs;
736*22028508SToomas Soome }
737*22028508SToomas Soome }
738*22028508SToomas Soome }
739*22028508SToomas Soome
740*22028508SToomas Soome cur = malloc(sizeof(*cur));
741*22028508SToomas Soome if (cur == NULL)
742*22028508SToomas Soome return (NULL);
743*22028508SToomas Soome memset(cur, 0, sizeof(*cur));
744*22028508SToomas Soome cur->tf_pkg = pkg;
745*22028508SToomas Soome
746*22028508SToomas Soome while (1) {
747*22028508SToomas Soome if (get_zipped(pkg, &cur->tf_hdr,
748*22028508SToomas Soome sizeof(cur->tf_hdr)) == -1) {
749*22028508SToomas Soome free(cur);
750*22028508SToomas Soome return (NULL);
751*22028508SToomas Soome }
752*22028508SToomas Soome
753*22028508SToomas Soome /*
754*22028508SToomas Soome * There are always 2 empty blocks appended to
755*22028508SToomas Soome * a PKG. It marks the end of the archive.
756*22028508SToomas Soome */
757*22028508SToomas Soome if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) {
758*22028508SToomas Soome free(cur);
759*22028508SToomas Soome errno = ENOSPC;
760*22028508SToomas Soome return (NULL);
761*22028508SToomas Soome }
762*22028508SToomas Soome
763*22028508SToomas Soome cur->tf_ofs = pkg->pkg_ofs;
764*22028508SToomas Soome cur->tf_size = pkg_atol(cur->tf_hdr.ut_size,
765*22028508SToomas Soome sizeof(cur->tf_hdr.ut_size));
766*22028508SToomas Soome
767*22028508SToomas Soome if (cur->tf_hdr.ut_name[0] != '+')
768*22028508SToomas Soome break;
769*22028508SToomas Soome
770*22028508SToomas Soome /*
771*22028508SToomas Soome * Skip package meta-files.
772*22028508SToomas Soome */
773*22028508SToomas Soome ofs = cur->tf_ofs + cur->tf_size;
774*22028508SToomas Soome ofs = (ofs + 0x1ff) & ~0x1ff;
775*22028508SToomas Soome while (pkg->pkg_ofs < ofs) {
776*22028508SToomas Soome if (get_zipped(pkg, buf, sizeof(buf)) == -1) {
777*22028508SToomas Soome free(cur);
778*22028508SToomas Soome return (NULL);
779*22028508SToomas Soome }
780*22028508SToomas Soome }
781*22028508SToomas Soome }
782*22028508SToomas Soome
783*22028508SToomas Soome if (last != NULL)
784*22028508SToomas Soome last->tf_next = cur;
785*22028508SToomas Soome else
786*22028508SToomas Soome pkg->pkg_first = cur;
787*22028508SToomas Soome pkg->pkg_last = cur;
788*22028508SToomas Soome }
789*22028508SToomas Soome
790*22028508SToomas Soome return (cur);
791*22028508SToomas Soome }
792