1*55fea89dSDan Cross /*-
2199767f8SToomas Soome * Copyright (c) 2007-2014, Juniper Networks, Inc.
3199767f8SToomas Soome * All rights reserved.
4199767f8SToomas Soome *
5199767f8SToomas Soome * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome * modification, are permitted provided that the following conditions
7199767f8SToomas Soome * are met:
8199767f8SToomas Soome * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome * notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome * notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome * documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome *
14199767f8SToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome * SUCH DAMAGE.
25199767f8SToomas Soome */
26199767f8SToomas Soome
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome __FBSDID("$FreeBSD$");
29199767f8SToomas Soome
30199767f8SToomas Soome #include "stand.h"
31199767f8SToomas Soome
32199767f8SToomas Soome #include <sys/stat.h>
33199767f8SToomas Soome #include <sys/stdint.h>
34199767f8SToomas Soome #include <string.h>
35199767f8SToomas Soome #include <zlib.h>
36199767f8SToomas Soome
37199767f8SToomas Soome #ifdef PKGFS_DEBUG
38199767f8SToomas Soome #define DBG(x) printf x
39199767f8SToomas Soome #else
40199767f8SToomas Soome #define DBG(x)
41199767f8SToomas Soome #endif
42199767f8SToomas Soome
43199767f8SToomas Soome static int pkg_open(const char *, struct open_file *);
44199767f8SToomas Soome static int pkg_close(struct open_file *);
45199767f8SToomas Soome static int pkg_read(struct open_file *, void *, size_t, size_t *);
46199767f8SToomas Soome static off_t pkg_seek(struct open_file *, off_t, int);
47199767f8SToomas Soome static int pkg_stat(struct open_file *, struct stat *);
48199767f8SToomas Soome static int pkg_readdir(struct open_file *, struct dirent *);
49199767f8SToomas Soome
50199767f8SToomas Soome struct fs_ops pkgfs_fsops = {
51199767f8SToomas Soome "pkg",
52*55fea89dSDan Cross pkg_open,
53*55fea89dSDan Cross pkg_close,
54199767f8SToomas Soome pkg_read,
55199767f8SToomas Soome null_write,
56199767f8SToomas Soome pkg_seek,
57199767f8SToomas Soome pkg_stat,
58199767f8SToomas Soome pkg_readdir
59199767f8SToomas Soome };
60199767f8SToomas Soome
61199767f8SToomas Soome #define PKG_BUFSIZE 512
62199767f8SToomas Soome #define PKG_MAXCACHESZ 4096
63199767f8SToomas Soome
64199767f8SToomas Soome #define PKG_FILEEXT ".tgz"
65199767f8SToomas Soome
66199767f8SToomas Soome /*
67199767f8SToomas Soome * Layout of POSIX 'ustar' header.
68199767f8SToomas Soome */
69199767f8SToomas Soome struct ustar_hdr {
70199767f8SToomas Soome char ut_name[100];
71199767f8SToomas Soome char ut_mode[8];
72199767f8SToomas Soome char ut_uid[8];
73199767f8SToomas Soome char ut_gid[8];
74199767f8SToomas Soome char ut_size[12];
75199767f8SToomas Soome char ut_mtime[12];
76199767f8SToomas Soome char ut_checksum[8];
77199767f8SToomas Soome char ut_typeflag[1];
78199767f8SToomas Soome char ut_linkname[100];
79199767f8SToomas Soome char ut_magic[6]; /* For POSIX: "ustar\0" */
80199767f8SToomas Soome char ut_version[2]; /* For POSIX: "00" */
81199767f8SToomas Soome char ut_uname[32];
82199767f8SToomas Soome char ut_gname[32];
83199767f8SToomas Soome char ut_rdevmajor[8];
84199767f8SToomas Soome char ut_rdevminor[8];
85199767f8SToomas Soome union {
86199767f8SToomas Soome struct {
87199767f8SToomas Soome char prefix[155];
88199767f8SToomas Soome } posix;
89199767f8SToomas Soome struct {
90199767f8SToomas Soome char atime[12];
91199767f8SToomas Soome char ctime[12];
92199767f8SToomas Soome char offset[12];
93199767f8SToomas Soome char longnames[4];
94199767f8SToomas Soome char unused[1];
95199767f8SToomas Soome struct gnu_sparse {
96199767f8SToomas Soome char offset[12];
97199767f8SToomas Soome char numbytes[12];
98199767f8SToomas Soome } sparse[4];
99199767f8SToomas Soome char isextended[1];
100199767f8SToomas Soome char realsize[12];
101199767f8SToomas Soome } gnu;
102199767f8SToomas Soome } u;
103199767f8SToomas Soome u_char __padding[12];
104199767f8SToomas Soome };
105199767f8SToomas Soome
106199767f8SToomas Soome struct package;
107199767f8SToomas Soome
108199767f8SToomas Soome struct tarfile
109199767f8SToomas Soome {
110199767f8SToomas Soome struct package *tf_pkg;
111199767f8SToomas Soome struct tarfile *tf_next;
112199767f8SToomas Soome struct ustar_hdr tf_hdr;
113199767f8SToomas Soome off_t tf_ofs;
114199767f8SToomas Soome off_t tf_size;
115199767f8SToomas Soome off_t tf_fp;
116199767f8SToomas Soome size_t tf_cachesz;
117199767f8SToomas Soome void *tf_cache;
118199767f8SToomas Soome };
119199767f8SToomas Soome
120199767f8SToomas Soome struct package
121199767f8SToomas Soome {
122199767f8SToomas Soome struct package *pkg_chain;
123199767f8SToomas Soome int pkg_fd;
124199767f8SToomas Soome off_t pkg_ofs;
125199767f8SToomas Soome z_stream pkg_zs;
126199767f8SToomas Soome struct tarfile *pkg_first;
127199767f8SToomas Soome struct tarfile *pkg_last;
128199767f8SToomas Soome u_char pkg_buf[PKG_BUFSIZE];
129199767f8SToomas Soome };
130199767f8SToomas Soome
131199767f8SToomas Soome static struct package *package = NULL;
132199767f8SToomas Soome
133199767f8SToomas Soome static int new_package(int, struct package **);
134199767f8SToomas Soome
135199767f8SToomas Soome void
pkgfs_cleanup(void)136199767f8SToomas Soome pkgfs_cleanup(void)
137199767f8SToomas Soome {
138199767f8SToomas Soome struct package *chain;
139199767f8SToomas Soome struct tarfile *tf, *tfn;
140199767f8SToomas Soome
141199767f8SToomas Soome while (package != NULL) {
142199767f8SToomas Soome inflateEnd(&package->pkg_zs);
143199767f8SToomas Soome close(package->pkg_fd);
144199767f8SToomas Soome
145199767f8SToomas Soome tf = package->pkg_first;
146199767f8SToomas Soome while (tf != NULL) {
147199767f8SToomas Soome tfn = tf->tf_next;
148199767f8SToomas Soome if (tf->tf_cachesz > 0)
149199767f8SToomas Soome free(tf->tf_cache);
150199767f8SToomas Soome free(tf);
151199767f8SToomas Soome tf = tfn;
152199767f8SToomas Soome }
153199767f8SToomas Soome
154199767f8SToomas Soome chain = package->pkg_chain;
155199767f8SToomas Soome free(package);
156199767f8SToomas Soome package = chain;
157199767f8SToomas Soome }
158199767f8SToomas Soome }
159199767f8SToomas Soome
160199767f8SToomas Soome int
pkgfs_init(const char * pkgname,struct fs_ops * proto)161199767f8SToomas Soome pkgfs_init(const char *pkgname, struct fs_ops *proto)
162199767f8SToomas Soome {
163199767f8SToomas Soome struct package *pkg;
164199767f8SToomas Soome int error, fd;
165199767f8SToomas Soome
166199767f8SToomas Soome if (proto != &pkgfs_fsops)
167199767f8SToomas Soome pkgfs_cleanup();
168199767f8SToomas Soome
169199767f8SToomas Soome exclusive_file_system = proto;
170199767f8SToomas Soome
171199767f8SToomas Soome fd = open(pkgname, O_RDONLY);
172199767f8SToomas Soome
173199767f8SToomas Soome exclusive_file_system = NULL;
174199767f8SToomas Soome
175199767f8SToomas Soome if (fd == -1)
176199767f8SToomas Soome return (errno);
177199767f8SToomas Soome
178199767f8SToomas Soome error = new_package(fd, &pkg);
179199767f8SToomas Soome if (error) {
180199767f8SToomas Soome close(fd);
181199767f8SToomas Soome return (error);
182199767f8SToomas Soome }
183199767f8SToomas Soome
184199767f8SToomas Soome if (pkg == NULL)
185199767f8SToomas Soome return (EDOOFUS);
186199767f8SToomas Soome
187199767f8SToomas Soome pkg->pkg_chain = package;
188199767f8SToomas Soome package = pkg;
189199767f8SToomas Soome exclusive_file_system = &pkgfs_fsops;
190199767f8SToomas Soome return (0);
191199767f8SToomas Soome }
192199767f8SToomas Soome
193199767f8SToomas Soome static int get_mode(struct tarfile *);
194199767f8SToomas Soome static int get_zipped(struct package *, void *, size_t);
195199767f8SToomas Soome static int new_package(int, struct package **);
196199767f8SToomas Soome static struct tarfile *scan_tarfile(struct package *, struct tarfile *);
197199767f8SToomas Soome
198199767f8SToomas Soome static int
pkg_open(const char * fn,struct open_file * f)199199767f8SToomas Soome pkg_open(const char *fn, struct open_file *f)
200199767f8SToomas Soome {
201199767f8SToomas Soome struct tarfile *tf;
202199767f8SToomas Soome
203199767f8SToomas Soome if (fn == NULL || f == NULL)
204199767f8SToomas Soome return (EINVAL);
205199767f8SToomas Soome
206199767f8SToomas Soome if (package == NULL)
207199767f8SToomas Soome return (ENXIO);
208199767f8SToomas Soome
209199767f8SToomas Soome /*
210199767f8SToomas Soome * We can only read from a package, so reject request to open
211199767f8SToomas Soome * for write-only or read-write.
212199767f8SToomas Soome */
213199767f8SToomas Soome if (f->f_flags != F_READ)
214199767f8SToomas Soome return (EPERM);
215199767f8SToomas Soome
216199767f8SToomas Soome /*
217199767f8SToomas Soome * Scan the file headers for the named file. We stop scanning
218199767f8SToomas Soome * at the first filename that has the .pkg extension. This is
219199767f8SToomas Soome * a package within a package. We assume we have all the files
220199767f8SToomas Soome * we need up-front and without having to dig within nested
221199767f8SToomas Soome * packages.
222199767f8SToomas Soome *
223199767f8SToomas Soome * Note that we preserve streaming properties as much as possible.
224199767f8SToomas Soome */
225199767f8SToomas Soome while (*fn == '/')
226199767f8SToomas Soome fn++;
227199767f8SToomas Soome
228199767f8SToomas Soome /*
229199767f8SToomas Soome * Allow opening of the root directory for use by readdir()
230199767f8SToomas Soome * to support listing files in the package.
231199767f8SToomas Soome */
232199767f8SToomas Soome if (*fn == '\0') {
233199767f8SToomas Soome f->f_fsdata = NULL;
234199767f8SToomas Soome return (0);
235199767f8SToomas Soome }
236199767f8SToomas Soome
237199767f8SToomas Soome tf = scan_tarfile(package, NULL);
238199767f8SToomas Soome while (tf != NULL) {
239199767f8SToomas Soome if (strcmp(fn, tf->tf_hdr.ut_name) == 0) {
240199767f8SToomas Soome f->f_fsdata = tf;
241199767f8SToomas Soome tf->tf_fp = 0; /* Reset the file pointer. */
242199767f8SToomas Soome return (0);
243199767f8SToomas Soome }
244199767f8SToomas Soome tf = scan_tarfile(package, tf);
245199767f8SToomas Soome }
246199767f8SToomas Soome return (errno);
247199767f8SToomas Soome }
248199767f8SToomas Soome
249199767f8SToomas Soome static int
pkg_close(struct open_file * f)250199767f8SToomas Soome pkg_close(struct open_file *f)
251199767f8SToomas Soome {
252199767f8SToomas Soome struct tarfile *tf;
253199767f8SToomas Soome
254199767f8SToomas Soome tf = (struct tarfile *)f->f_fsdata;
255199767f8SToomas Soome if (tf == NULL)
256199767f8SToomas Soome return (0);
257199767f8SToomas Soome
258199767f8SToomas Soome /*
259199767f8SToomas Soome * Free up the cache if we read all of the file.
260199767f8SToomas Soome */
261199767f8SToomas Soome if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) {
262199767f8SToomas Soome free(tf->tf_cache);
263199767f8SToomas Soome tf->tf_cachesz = 0;
264199767f8SToomas Soome }
265199767f8SToomas Soome return (0);
266199767f8SToomas Soome }
267199767f8SToomas Soome
268199767f8SToomas Soome static int
pkg_read(struct open_file * f,void * buf,size_t size,size_t * res)269199767f8SToomas Soome pkg_read(struct open_file *f, void *buf, size_t size, size_t *res)
270199767f8SToomas Soome {
271199767f8SToomas Soome struct tarfile *tf;
272199767f8SToomas Soome char *p;
273199767f8SToomas Soome off_t fp;
274199767f8SToomas Soome size_t sz;
275199767f8SToomas Soome
276199767f8SToomas Soome tf = (struct tarfile *)f->f_fsdata;
277199767f8SToomas Soome if (tf == NULL) {
278199767f8SToomas Soome if (res != NULL)
279199767f8SToomas Soome *res = size;
280199767f8SToomas Soome return (EBADF);
281199767f8SToomas Soome }
282199767f8SToomas Soome
283199767f8SToomas Soome fp = tf->tf_fp;
284199767f8SToomas Soome p = buf;
285199767f8SToomas Soome sz = 0;
286199767f8SToomas Soome while (size > 0) {
287199767f8SToomas Soome sz = tf->tf_size - fp;
288199767f8SToomas Soome if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size)
289199767f8SToomas Soome sz = tf->tf_cachesz - fp;
290199767f8SToomas Soome if (size < sz)
291199767f8SToomas Soome sz = size;
292199767f8SToomas Soome if (sz == 0)
293199767f8SToomas Soome break;
294199767f8SToomas Soome
295199767f8SToomas Soome if (fp < tf->tf_cachesz) {
296199767f8SToomas Soome /* Satisfy the request from cache. */
297199767f8SToomas Soome memcpy(p, tf->tf_cache + fp, sz);
298199767f8SToomas Soome fp += sz;
299199767f8SToomas Soome p += sz;
300199767f8SToomas Soome size -= sz;
301199767f8SToomas Soome continue;
302199767f8SToomas Soome }
303199767f8SToomas Soome
304199767f8SToomas Soome if (get_zipped(tf->tf_pkg, p, sz) == -1) {
305199767f8SToomas Soome sz = -1;
306199767f8SToomas Soome break;
307199767f8SToomas Soome }
308199767f8SToomas Soome
309199767f8SToomas Soome fp += sz;
310199767f8SToomas Soome p += sz;
311199767f8SToomas Soome size -= sz;
312199767f8SToomas Soome
313199767f8SToomas Soome if (tf->tf_cachesz != 0)
314199767f8SToomas Soome continue;
315199767f8SToomas Soome
316199767f8SToomas Soome tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ;
317199767f8SToomas Soome tf->tf_cache = malloc(tf->tf_cachesz);
318199767f8SToomas Soome if (tf->tf_cache != NULL)
319199767f8SToomas Soome memcpy(tf->tf_cache, buf, tf->tf_cachesz);
320199767f8SToomas Soome else
321199767f8SToomas Soome tf->tf_cachesz = 0;
322199767f8SToomas Soome }
323199767f8SToomas Soome
324199767f8SToomas Soome tf->tf_fp = fp;
325199767f8SToomas Soome if (res != NULL)
326199767f8SToomas Soome *res = size;
327199767f8SToomas Soome return ((sz == -1) ? errno : 0);
328199767f8SToomas Soome }
329199767f8SToomas Soome
330199767f8SToomas Soome static off_t
pkg_seek(struct open_file * f,off_t ofs,int whence)331199767f8SToomas Soome pkg_seek(struct open_file *f, off_t ofs, int whence)
332199767f8SToomas Soome {
333199767f8SToomas Soome char buf[512];
334199767f8SToomas Soome struct tarfile *tf;
335199767f8SToomas Soome off_t delta;
336199767f8SToomas Soome size_t sz, res;
337199767f8SToomas Soome int error;
338199767f8SToomas Soome
339199767f8SToomas Soome tf = (struct tarfile *)f->f_fsdata;
340199767f8SToomas Soome if (tf == NULL) {
341199767f8SToomas Soome errno = EBADF;
342199767f8SToomas Soome return (-1);
343199767f8SToomas Soome }
344199767f8SToomas Soome
345199767f8SToomas Soome switch (whence) {
346199767f8SToomas Soome case SEEK_SET:
347199767f8SToomas Soome delta = ofs - tf->tf_fp;
348199767f8SToomas Soome break;
349199767f8SToomas Soome case SEEK_CUR:
350199767f8SToomas Soome delta = ofs;
351199767f8SToomas Soome break;
352199767f8SToomas Soome case SEEK_END:
353199767f8SToomas Soome delta = tf->tf_size - tf->tf_fp + ofs;
354199767f8SToomas Soome break;
355199767f8SToomas Soome default:
356199767f8SToomas Soome errno = EINVAL;
357199767f8SToomas Soome return (-1);
358199767f8SToomas Soome }
359199767f8SToomas Soome
360199767f8SToomas Soome if (delta < 0) {
361199767f8SToomas Soome DBG(("%s: negative file seek (%jd)\n", __func__,
362199767f8SToomas Soome (intmax_t)delta));
363199767f8SToomas Soome errno = ESPIPE;
364199767f8SToomas Soome return (-1);
365199767f8SToomas Soome }
366199767f8SToomas Soome
367199767f8SToomas Soome while (delta > 0 && tf->tf_fp < tf->tf_size) {
368199767f8SToomas Soome sz = (delta > sizeof(buf)) ? sizeof(buf) : delta;
369199767f8SToomas Soome error = pkg_read(f, buf, sz, &res);
370199767f8SToomas Soome if (error != 0) {
371199767f8SToomas Soome errno = error;
372199767f8SToomas Soome return (-1);
373199767f8SToomas Soome }
374199767f8SToomas Soome delta -= sz - res;
375199767f8SToomas Soome }
376199767f8SToomas Soome
377199767f8SToomas Soome return (tf->tf_fp);
378199767f8SToomas Soome }
379199767f8SToomas Soome
380199767f8SToomas Soome static int
pkg_stat(struct open_file * f,struct stat * sb)381199767f8SToomas Soome pkg_stat(struct open_file *f, struct stat *sb)
382199767f8SToomas Soome {
383199767f8SToomas Soome struct tarfile *tf;
384199767f8SToomas Soome
385199767f8SToomas Soome tf = (struct tarfile *)f->f_fsdata;
386199767f8SToomas Soome if (tf == NULL)
387199767f8SToomas Soome return (EBADF);
388199767f8SToomas Soome memset(sb, 0, sizeof(*sb));
389199767f8SToomas Soome sb->st_mode = get_mode(tf);
390199767f8SToomas Soome sb->st_size = tf->tf_size;
391199767f8SToomas Soome sb->st_blocks = (tf->tf_size + 511) / 512;
392199767f8SToomas Soome return (0);
393199767f8SToomas Soome }
394199767f8SToomas Soome
395199767f8SToomas Soome static int
pkg_readdir(struct open_file * f,struct dirent * d)396199767f8SToomas Soome pkg_readdir(struct open_file *f, struct dirent *d)
397199767f8SToomas Soome {
398199767f8SToomas Soome struct tarfile *tf;
399199767f8SToomas Soome
400199767f8SToomas Soome tf = (struct tarfile *)f->f_fsdata;
401199767f8SToomas Soome if (tf != NULL)
402199767f8SToomas Soome return (EBADF);
403199767f8SToomas Soome
404199767f8SToomas Soome tf = scan_tarfile(package, NULL);
405199767f8SToomas Soome if (tf == NULL)
406199767f8SToomas Soome return (ENOENT);
407199767f8SToomas Soome
408199767f8SToomas Soome d->d_fileno = 0;
409199767f8SToomas Soome d->d_reclen = sizeof(*d);
410199767f8SToomas Soome d->d_type = DT_REG;
411199767f8SToomas Soome memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name));
412199767f8SToomas Soome return (0);
413199767f8SToomas Soome }
414199767f8SToomas Soome
415199767f8SToomas Soome /*
416199767f8SToomas Soome * Low-level support functions.
417199767f8SToomas Soome */
418199767f8SToomas Soome
419199767f8SToomas Soome static int
get_byte(struct package * pkg,off_t * op)420199767f8SToomas Soome get_byte(struct package *pkg, off_t *op)
421199767f8SToomas Soome {
422199767f8SToomas Soome int c;
423199767f8SToomas Soome
424199767f8SToomas Soome if (pkg->pkg_zs.avail_in == 0) {
425199767f8SToomas Soome c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
426199767f8SToomas Soome if (c <= 0)
427199767f8SToomas Soome return (-1);
428199767f8SToomas Soome pkg->pkg_zs.avail_in = c;
429199767f8SToomas Soome pkg->pkg_zs.next_in = pkg->pkg_buf;
430199767f8SToomas Soome }
431199767f8SToomas Soome
432199767f8SToomas Soome c = *pkg->pkg_zs.next_in;
433199767f8SToomas Soome pkg->pkg_zs.next_in++;
434199767f8SToomas Soome pkg->pkg_zs.avail_in--;
435199767f8SToomas Soome (*op)++;
436199767f8SToomas Soome return (c);
437199767f8SToomas Soome }
438199767f8SToomas Soome
439199767f8SToomas Soome static int
get_zipped(struct package * pkg,void * buf,size_t bufsz)440199767f8SToomas Soome get_zipped(struct package *pkg, void *buf, size_t bufsz)
441199767f8SToomas Soome {
442199767f8SToomas Soome int c;
443199767f8SToomas Soome
444199767f8SToomas Soome pkg->pkg_zs.next_out = buf;
445199767f8SToomas Soome pkg->pkg_zs.avail_out = bufsz;
446199767f8SToomas Soome
447199767f8SToomas Soome while (pkg->pkg_zs.avail_out) {
448199767f8SToomas Soome if (pkg->pkg_zs.avail_in == 0) {
449199767f8SToomas Soome c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
450199767f8SToomas Soome if (c <= 0) {
451199767f8SToomas Soome errno = EIO;
452199767f8SToomas Soome return (-1);
453199767f8SToomas Soome }
454199767f8SToomas Soome pkg->pkg_zs.avail_in = c;
455199767f8SToomas Soome pkg->pkg_zs.next_in = pkg->pkg_buf;
456199767f8SToomas Soome }
457199767f8SToomas Soome
458199767f8SToomas Soome c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH);
459199767f8SToomas Soome if (c != Z_OK && c != Z_STREAM_END) {
460199767f8SToomas Soome errno = EIO;
461199767f8SToomas Soome return (-1);
462199767f8SToomas Soome }
463199767f8SToomas Soome }
464199767f8SToomas Soome
465199767f8SToomas Soome pkg->pkg_ofs += bufsz;
466199767f8SToomas Soome return (0);
467199767f8SToomas Soome }
468199767f8SToomas Soome
469199767f8SToomas Soome static int
cache_data(struct tarfile * tf)470199767f8SToomas Soome cache_data(struct tarfile *tf)
471199767f8SToomas Soome {
472199767f8SToomas Soome struct package *pkg;
473199767f8SToomas Soome size_t sz;
474199767f8SToomas Soome
475199767f8SToomas Soome if (tf == NULL) {
476199767f8SToomas Soome DBG(("%s: no file to cache data for?\n", __func__));
477199767f8SToomas Soome errno = EINVAL;
478199767f8SToomas Soome return (-1);
479199767f8SToomas Soome }
480199767f8SToomas Soome
481199767f8SToomas Soome pkg = tf->tf_pkg;
482199767f8SToomas Soome if (pkg == NULL) {
483199767f8SToomas Soome DBG(("%s: no package associated with file?\n", __func__));
484199767f8SToomas Soome errno = EINVAL;
485199767f8SToomas Soome return (-1);
486199767f8SToomas Soome }
487199767f8SToomas Soome
488199767f8SToomas Soome if (tf->tf_ofs != pkg->pkg_ofs) {
489199767f8SToomas Soome DBG(("%s: caching after partial read of file %s?\n",
490199767f8SToomas Soome __func__, tf->tf_hdr.ut_name));
491199767f8SToomas Soome errno = EINVAL;
492199767f8SToomas Soome return (-1);
493199767f8SToomas Soome }
494199767f8SToomas Soome
495199767f8SToomas Soome /* We don't cache everything... */
496199767f8SToomas Soome if (tf->tf_size > PKG_MAXCACHESZ) {
497199767f8SToomas Soome errno = ENOMEM;
498199767f8SToomas Soome return (-1);
499199767f8SToomas Soome }
500199767f8SToomas Soome
501199767f8SToomas Soome /* All files are padded to a multiple of 512 bytes. */
502199767f8SToomas Soome sz = (tf->tf_size + 0x1ff) & ~0x1ff;
503199767f8SToomas Soome
504199767f8SToomas Soome tf->tf_cache = malloc(sz);
505199767f8SToomas Soome if (tf->tf_cache == NULL) {
506199767f8SToomas Soome DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz));
507199767f8SToomas Soome errno = ENOMEM;
508199767f8SToomas Soome return (-1);
509199767f8SToomas Soome }
510199767f8SToomas Soome
511199767f8SToomas Soome tf->tf_cachesz = sz;
512199767f8SToomas Soome return (get_zipped(pkg, tf->tf_cache, sz));
513199767f8SToomas Soome }
514199767f8SToomas Soome
515199767f8SToomas Soome /*
516199767f8SToomas Soome * Note that this implementation does not (and should not!) obey
517199767f8SToomas Soome * locale settings; you cannot simply substitute strtol here, since
518199767f8SToomas Soome * it does obey locale.
519199767f8SToomas Soome */
520199767f8SToomas Soome static off_t
pkg_atol8(const char * p,unsigned char_cnt)521199767f8SToomas Soome pkg_atol8(const char *p, unsigned char_cnt)
522199767f8SToomas Soome {
523199767f8SToomas Soome int64_t l, limit, last_digit_limit;
524199767f8SToomas Soome int digit, sign, base;
525199767f8SToomas Soome
526199767f8SToomas Soome base = 8;
527199767f8SToomas Soome limit = INT64_MAX / base;
528199767f8SToomas Soome last_digit_limit = INT64_MAX % base;
529199767f8SToomas Soome
530199767f8SToomas Soome while (*p == ' ' || *p == '\t')
531199767f8SToomas Soome p++;
532199767f8SToomas Soome if (*p == '-') {
533199767f8SToomas Soome sign = -1;
534199767f8SToomas Soome p++;
535199767f8SToomas Soome } else
536199767f8SToomas Soome sign = 1;
537199767f8SToomas Soome
538199767f8SToomas Soome l = 0;
539199767f8SToomas Soome digit = *p - '0';
540199767f8SToomas Soome while (digit >= 0 && digit < base && char_cnt-- > 0) {
541199767f8SToomas Soome if (l>limit || (l == limit && digit > last_digit_limit)) {
542199767f8SToomas Soome l = UINT64_MAX; /* Truncate on overflow. */
543199767f8SToomas Soome break;
544199767f8SToomas Soome }
545199767f8SToomas Soome l = (l * base) + digit;
546199767f8SToomas Soome digit = *++p - '0';
547199767f8SToomas Soome }
548199767f8SToomas Soome return (sign < 0) ? -l : l;
549199767f8SToomas Soome }
550199767f8SToomas Soome
551199767f8SToomas Soome /*
552199767f8SToomas Soome * Parse a base-256 integer. This is just a straight signed binary
553199767f8SToomas Soome * value in big-endian order, except that the high-order bit is
554199767f8SToomas Soome * ignored. Remember that "int64_t" may or may not be exactly 64
555199767f8SToomas Soome * bits; the implementation here tries to avoid making any assumptions
556199767f8SToomas Soome * about the actual size of an int64_t. It does assume we're using
557199767f8SToomas Soome * twos-complement arithmetic, though.
558199767f8SToomas Soome */
559199767f8SToomas Soome static int64_t
pkg_atol256(const char * _p,unsigned char_cnt)560199767f8SToomas Soome pkg_atol256(const char *_p, unsigned char_cnt)
561199767f8SToomas Soome {
562199767f8SToomas Soome int64_t l, upper_limit, lower_limit;
563199767f8SToomas Soome const unsigned char *p = (const unsigned char *)_p;
564199767f8SToomas Soome
565199767f8SToomas Soome upper_limit = INT64_MAX / 256;
566199767f8SToomas Soome lower_limit = INT64_MIN / 256;
567199767f8SToomas Soome
568199767f8SToomas Soome /* Pad with 1 or 0 bits, depending on sign. */
569199767f8SToomas Soome if ((0x40 & *p) == 0x40)
570199767f8SToomas Soome l = (int64_t)-1;
571199767f8SToomas Soome else
572199767f8SToomas Soome l = 0;
573199767f8SToomas Soome l = (l << 6) | (0x3f & *p++);
574199767f8SToomas Soome while (--char_cnt > 0) {
575199767f8SToomas Soome if (l > upper_limit) {
576199767f8SToomas Soome l = INT64_MAX; /* Truncate on overflow */
577199767f8SToomas Soome break;
578199767f8SToomas Soome } else if (l < lower_limit) {
579199767f8SToomas Soome l = INT64_MIN;
580199767f8SToomas Soome break;
581199767f8SToomas Soome }
582199767f8SToomas Soome l = (l << 8) | (0xff & (int64_t)*p++);
583199767f8SToomas Soome }
584199767f8SToomas Soome return (l);
585199767f8SToomas Soome }
586199767f8SToomas Soome
587199767f8SToomas Soome static off_t
pkg_atol(const char * p,unsigned char_cnt)588199767f8SToomas Soome pkg_atol(const char *p, unsigned char_cnt)
589199767f8SToomas Soome {
590199767f8SToomas Soome /*
591199767f8SToomas Soome * Technically, GNU pkg considers a field to be in base-256
592199767f8SToomas Soome * only if the first byte is 0xff or 0x80.
593199767f8SToomas Soome */
594199767f8SToomas Soome if (*p & 0x80)
595199767f8SToomas Soome return (pkg_atol256(p, char_cnt));
596199767f8SToomas Soome return (pkg_atol8(p, char_cnt));
597199767f8SToomas Soome }
598199767f8SToomas Soome
599199767f8SToomas Soome static int
get_mode(struct tarfile * tf)600199767f8SToomas Soome get_mode(struct tarfile *tf)
601199767f8SToomas Soome {
602199767f8SToomas Soome return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode)));
603199767f8SToomas Soome }
604199767f8SToomas Soome
605199767f8SToomas Soome /* GZip flag byte */
606199767f8SToomas Soome #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
607199767f8SToomas Soome #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
608199767f8SToomas Soome #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
609199767f8SToomas Soome #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
610199767f8SToomas Soome #define COMMENT 0x10 /* bit 4 set: file comment present */
611199767f8SToomas Soome #define RESERVED 0xE0 /* bits 5..7: reserved */
612199767f8SToomas Soome
613199767f8SToomas Soome static int
new_package(int fd,struct package ** pp)614199767f8SToomas Soome new_package(int fd, struct package **pp)
615199767f8SToomas Soome {
616199767f8SToomas Soome struct package *pkg;
617199767f8SToomas Soome off_t ofs;
618199767f8SToomas Soome int flags, i, error;
619199767f8SToomas Soome
620199767f8SToomas Soome pkg = malloc(sizeof(*pkg));
621199767f8SToomas Soome if (pkg == NULL)
622199767f8SToomas Soome return (ENOMEM);
623199767f8SToomas Soome
624199767f8SToomas Soome bzero(pkg, sizeof(*pkg));
625199767f8SToomas Soome pkg->pkg_fd = fd;
626199767f8SToomas Soome
627199767f8SToomas Soome /*
628199767f8SToomas Soome * Parse the header.
629199767f8SToomas Soome */
630199767f8SToomas Soome error = EFTYPE;
631199767f8SToomas Soome ofs = 0;
632199767f8SToomas Soome
633199767f8SToomas Soome /* Check megic. */
634199767f8SToomas Soome if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b)
635199767f8SToomas Soome goto fail;
636199767f8SToomas Soome /* Check method. */
637199767f8SToomas Soome if (get_byte(pkg, &ofs) != Z_DEFLATED)
638199767f8SToomas Soome goto fail;
639199767f8SToomas Soome /* Check flags. */
640199767f8SToomas Soome flags = get_byte(pkg, &ofs);
641199767f8SToomas Soome if (flags & RESERVED)
642199767f8SToomas Soome goto fail;
643199767f8SToomas Soome
644199767f8SToomas Soome /* Skip time, xflags and OS code. */
645199767f8SToomas Soome for (i = 0; i < 6; i++) {
646199767f8SToomas Soome if (get_byte(pkg, &ofs) == -1)
647199767f8SToomas Soome goto fail;
648199767f8SToomas Soome }
649199767f8SToomas Soome
650199767f8SToomas Soome /* Skip extra field. */
651199767f8SToomas Soome if (flags & EXTRA_FIELD) {
652199767f8SToomas Soome i = (get_byte(pkg, &ofs) & 0xff) |
653199767f8SToomas Soome ((get_byte(pkg, &ofs) << 8) & 0xff);
654199767f8SToomas Soome while (i-- > 0) {
655199767f8SToomas Soome if (get_byte(pkg, &ofs) == -1)
656199767f8SToomas Soome goto fail;
657199767f8SToomas Soome }
658199767f8SToomas Soome }
659199767f8SToomas Soome
660199767f8SToomas Soome /* Skip original file name. */
661199767f8SToomas Soome if (flags & ORIG_NAME) {
662199767f8SToomas Soome do {
663199767f8SToomas Soome i = get_byte(pkg, &ofs);
664199767f8SToomas Soome } while (i != 0 && i != -1);
665199767f8SToomas Soome if (i == -1)
666199767f8SToomas Soome goto fail;
667199767f8SToomas Soome }
668199767f8SToomas Soome
669199767f8SToomas Soome /* Print the comment if it's there. */
670199767f8SToomas Soome if (flags & COMMENT) {
671199767f8SToomas Soome while (1) {
672199767f8SToomas Soome i = get_byte(pkg, &ofs);
673199767f8SToomas Soome if (i == -1)
674199767f8SToomas Soome goto fail;
675199767f8SToomas Soome if (i == 0)
676199767f8SToomas Soome break;
677199767f8SToomas Soome putchar(i);
678199767f8SToomas Soome }
679199767f8SToomas Soome }
680199767f8SToomas Soome
681199767f8SToomas Soome /* Skip the CRC. */
682199767f8SToomas Soome if (flags & HEAD_CRC) {
683199767f8SToomas Soome if (get_byte(pkg, &ofs) == -1)
684199767f8SToomas Soome goto fail;
685199767f8SToomas Soome if (get_byte(pkg, &ofs) == -1)
686199767f8SToomas Soome goto fail;
687199767f8SToomas Soome }
688199767f8SToomas Soome
689199767f8SToomas Soome /*
690199767f8SToomas Soome * Done parsing the ZIP header. Spkgt the inflation engine.
691199767f8SToomas Soome */
692199767f8SToomas Soome error = inflateInit2(&pkg->pkg_zs, -15);
693199767f8SToomas Soome if (error != Z_OK)
694199767f8SToomas Soome goto fail;
695199767f8SToomas Soome
696199767f8SToomas Soome *pp = pkg;
697199767f8SToomas Soome return (0);
698199767f8SToomas Soome
699199767f8SToomas Soome fail:
700199767f8SToomas Soome free(pkg);
701199767f8SToomas Soome return (error);
702199767f8SToomas Soome }
703199767f8SToomas Soome
704199767f8SToomas Soome static struct tarfile *
scan_tarfile(struct package * pkg,struct tarfile * last)705199767f8SToomas Soome scan_tarfile(struct package *pkg, struct tarfile *last)
706199767f8SToomas Soome {
707199767f8SToomas Soome char buf[512];
708199767f8SToomas Soome struct tarfile *cur;
709199767f8SToomas Soome off_t ofs;
710199767f8SToomas Soome size_t sz;
711199767f8SToomas Soome
712199767f8SToomas Soome cur = (last != NULL) ? last->tf_next : pkg->pkg_first;
713199767f8SToomas Soome if (cur == NULL) {
714199767f8SToomas Soome ofs = (last != NULL) ? last->tf_ofs + last->tf_size :
715199767f8SToomas Soome pkg->pkg_ofs;
716199767f8SToomas Soome ofs = (ofs + 0x1ff) & ~0x1ff;
717199767f8SToomas Soome
718199767f8SToomas Soome /* Check if we've reached EOF. */
719199767f8SToomas Soome if (ofs < pkg->pkg_ofs) {
720199767f8SToomas Soome errno = ENOSPC;
721199767f8SToomas Soome return (NULL);
722199767f8SToomas Soome }
723199767f8SToomas Soome
724199767f8SToomas Soome if (ofs != pkg->pkg_ofs) {
725199767f8SToomas Soome if (last != NULL && pkg->pkg_ofs == last->tf_ofs) {
726199767f8SToomas Soome if (cache_data(last) == -1)
727199767f8SToomas Soome return (NULL);
728199767f8SToomas Soome } else {
729199767f8SToomas Soome sz = ofs - pkg->pkg_ofs;
730199767f8SToomas Soome while (sz != 0) {
731199767f8SToomas Soome if (sz > sizeof(buf))
732199767f8SToomas Soome sz = sizeof(buf);
733199767f8SToomas Soome if (get_zipped(pkg, buf, sz) == -1)
734199767f8SToomas Soome return (NULL);
735199767f8SToomas Soome sz = ofs - pkg->pkg_ofs;
736199767f8SToomas Soome }
737199767f8SToomas Soome }
738199767f8SToomas Soome }
739199767f8SToomas Soome
740199767f8SToomas Soome cur = malloc(sizeof(*cur));
741199767f8SToomas Soome if (cur == NULL)
742199767f8SToomas Soome return (NULL);
743199767f8SToomas Soome memset(cur, 0, sizeof(*cur));
744199767f8SToomas Soome cur->tf_pkg = pkg;
745199767f8SToomas Soome
746199767f8SToomas Soome while (1) {
747199767f8SToomas Soome if (get_zipped(pkg, &cur->tf_hdr,
748199767f8SToomas Soome sizeof(cur->tf_hdr)) == -1) {
749199767f8SToomas Soome free(cur);
750199767f8SToomas Soome return (NULL);
751199767f8SToomas Soome }
752199767f8SToomas Soome
753199767f8SToomas Soome /*
754199767f8SToomas Soome * There are always 2 empty blocks appended to
755199767f8SToomas Soome * a PKG. It marks the end of the archive.
756199767f8SToomas Soome */
757199767f8SToomas Soome if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) {
758199767f8SToomas Soome free(cur);
759199767f8SToomas Soome errno = ENOSPC;
760199767f8SToomas Soome return (NULL);
761199767f8SToomas Soome }
762199767f8SToomas Soome
763199767f8SToomas Soome cur->tf_ofs = pkg->pkg_ofs;
764199767f8SToomas Soome cur->tf_size = pkg_atol(cur->tf_hdr.ut_size,
765199767f8SToomas Soome sizeof(cur->tf_hdr.ut_size));
766199767f8SToomas Soome
767199767f8SToomas Soome if (cur->tf_hdr.ut_name[0] != '+')
768199767f8SToomas Soome break;
769199767f8SToomas Soome
770199767f8SToomas Soome /*
771199767f8SToomas Soome * Skip package meta-files.
772199767f8SToomas Soome */
773199767f8SToomas Soome ofs = cur->tf_ofs + cur->tf_size;
774199767f8SToomas Soome ofs = (ofs + 0x1ff) & ~0x1ff;
775199767f8SToomas Soome while (pkg->pkg_ofs < ofs) {
776199767f8SToomas Soome if (get_zipped(pkg, buf, sizeof(buf)) == -1) {
777199767f8SToomas Soome free(cur);
778199767f8SToomas Soome return (NULL);
779199767f8SToomas Soome }
780199767f8SToomas Soome }
781199767f8SToomas Soome }
782199767f8SToomas Soome
783199767f8SToomas Soome if (last != NULL)
784199767f8SToomas Soome last->tf_next = cur;
785199767f8SToomas Soome else
786199767f8SToomas Soome pkg->pkg_first = cur;
787199767f8SToomas Soome pkg->pkg_last = cur;
788199767f8SToomas Soome }
789199767f8SToomas Soome
790199767f8SToomas Soome return (cur);
791199767f8SToomas Soome }
792