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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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