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