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