master xplshn/aruu / cmd / posix / pax.c
   1
   2
   3/* taken from: https://github.com/michaelforney/pax */
   4#ifndef _GNU_SOURCE
   5#define _GNU_SOURCE /* needed for major/minor (non-posix) */
   6#endif
   7
   8#include "arg.h"
   9
  10#include <assert.h>
  11#include <cpio.h>
  12#include <ctype.h>
  13#include <dirent.h>
  14#include <errno.h>
  15#include <fcntl.h>
  16#include <fnmatch.h>
  17#include <grp.h>
  18#include <limits.h>
  19#include <pwd.h>
  20#include <regex.h>
  21#include <spawn.h>
  22#include <stdarg.h>
  23#include <stdint.h>
  24#include <stdio.h>
  25#include <stdlib.h>
  26#include <string.h>
  27#include <sys/stat.h>
  28#include <sys/sysmacros.h>
  29#include <sys/types.h>
  30#include <sys/uio.h>
  31#include <sys/wait.h>
  32#include <tar.h>
  33#include <time.h>
  34#include <unistd.h>
  35
  36#ifndef O_SEARCH /* not present on some bsds */
  37#define O_SEARCH 0
  38#endif
  39
  40#if __APPLE__ /* macos lacks st_*tim from posix.1-2008 */
  41#define st_atim st_atimespec
  42#define st_ctim st_ctimespec
  43#define st_mtim st_mtimespec
  44#endif
  45
  46#ifndef HAVE_REALLOCARRAY
  47static void *
  48pax_reallocarray(void *p, size_t n, size_t m)
  49{
  50  if (m && n > SIZE_MAX / m) {
  51    errno = ENOMEM;
  52    return NULL;
  53  }
  54  return realloc(p, n * m);
  55}
  56#undef reallocarray
  57#define reallocarray pax_reallocarray
  58#endif
  59
  60#ifndef HAVE_PIPE2
  61static int
  62pax_pipe2(int fd[2], int flag)
  63{
  64  assert((flag | O_CLOEXEC) == O_CLOEXEC);
  65  if (pipe(fd) != 0)
  66    return -1;
  67  if (flag & O_CLOEXEC
  68      && (fcntl(fd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(fd[1], F_SETFD, FD_CLOEXEC) != 0))
  69    return -1;
  70  return 0;
  71}
  72#undef pipe2
  73#define pipe2 pax_pipe2
  74#endif
  75
  76#define LEN(a)        (sizeof(a) / sizeof *(a))
  77#define ROUNDUP(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
  78#define MAXTIME       077777777777
  79#define MAXSIZE       077777777777
  80#define MAXUGID       07777777
  81
  82enum mode {
  83  LIST,
  84  READ,
  85  WRITE,
  86  COPY,
  87};
  88
  89enum format {
  90  CPIO,
  91  PAX,
  92  USTAR,
  93  GNUTAR,
  94  V7,
  95};
  96
  97enum field {
  98  ATIME    = 1 << 0,
  99  CTIME    = 1 << 1,
 100  GID      = 1 << 2,
 101  GNAME    = 1 << 3,
 102  LINKPATH = 1 << 4,
 103  MODE     = 1 << 5,
 104  MTIME    = 1 << 6,
 105  PATH     = 1 << 7,
 106  SIZE     = 1 << 8,
 107  UID      = 1 << 9,
 108  UNAME    = 1 << 10,
 109};
 110
 111struct keyword {
 112  const char *name;
 113  enum field  field;
 114};
 115
 116struct strbuf {
 117  char  *str;
 118  size_t len, cap;
 119};
 120
 121struct header {
 122  /* keywords present in this header */
 123  enum field fields;
 124  /* keywords ignored because they were overridden by an option */
 125  enum field delete;
 126
 127  char type;
 128
 129  char           *path;
 130  size_t          pathlen;
 131  dev_t           dev;
 132  ino_t           ino;
 133  mode_t          mode;
 134  uid_t           uid;
 135  gid_t           gid;
 136  nlink_t         nlink;
 137  dev_t           rdev;
 138  off_t           size;
 139  struct timespec atime, mtime, ctime;
 140  char           *link;
 141  size_t          linklen;
 142  char           *uname;
 143  char           *gname;
 144
 145  struct strbuf pathbuf;
 146  struct strbuf linkbuf;
 147  struct strbuf unamebuf;
 148  struct strbuf gnamebuf;
 149
 150  /* tar-specific, pre-calculated split point between name and prefix */
 151  char *slash;
 152  /* read this data instead of stdin */
 153  char *data;
 154  /* source file path and flags for hard link (-l flag) */
 155  char           *file;
 156  struct timespec fileatime;
 157  int             flag;
 158};
 159
 160struct account {
 161  char      *name;
 162  enum field type;
 163  uid_t      uid;
 164  gid_t      gid;
 165};
 166
 167struct bufio {
 168  int   fd, err;
 169  off_t off;
 170  char  buf[64 * 1024];
 171  char *pos, *end;
 172};
 173
 174struct replstr {
 175  regex_t old;
 176  char *new;
 177  int             global;
 178  int             print;
 179  int             symlink;
 180  struct replstr *next;
 181};
 182
 183struct file {
 184  size_t       namelen;
 185  size_t       pathlen;
 186  dev_t        dev;
 187  struct file *next;
 188  char         name[];
 189};
 190
 191struct filelist {
 192  FILE        *input;
 193  struct file *pending;
 194};
 195
 196typedef int  readfn(struct bufio *, struct header *);
 197typedef void writefn(FILE *, struct header *);
 198
 199static int                  exitstatus;
 200static int                  aflag;
 201static int                  cflag;
 202static int                  dflag;
 203static int                  iflag;
 204static int                  kflag;
 205static int                  lflag;
 206static int                  nflag;
 207static int                  tflag;
 208static int                  uflag;
 209static int                  vflag;
 210static int                  Xflag;
 211static int                  follow;
 212static int                  preserve   = ATIME | MTIME;
 213static const struct keyword keywords[] = {
 214    {"atime", ATIME},
 215    {"ctime", CTIME},
 216    {"gid", GID},
 217    {"gname", GNAME},
 218    {"linkpath", LINKPATH},
 219    {"mtime", MTIME},
 220    {"path", PATH},
 221    {"size", SIZE},
 222    {"uid", UID},
 223    {"uname", UNAME},
 224};
 225static struct {
 226  enum field delete;
 227  int         linkdata;
 228  const char *listopt;
 229  const char *exthdrname;
 230  const char *globexthdrname;
 231  const char *invalid;
 232  int         times;
 233} opt;
 234static struct header   exthdr, globexthdr;
 235static struct replstr *replstr;
 236static time_t          curtime;
 237static char          **pats;
 238static size_t          patslen;
 239static int            *patsused;
 240static struct filelist files;
 241static struct bufio    bioin;
 242static char           *dest   = "";
 243static int             destfd = AT_FDCWD;
 244
 245static void
 246fatal(const char *fmt, ...)
 247{
 248  va_list ap;
 249
 250  if (fmt) {
 251    va_start(ap, fmt);
 252    vfprintf(stderr, fmt, ap);
 253    va_end(ap);
 254    if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
 255      fputc(' ', stderr);
 256      perror(NULL);
 257    } else {
 258      fputc('\n', stderr);
 259    }
 260  } else {
 261    perror(NULL);
 262  }
 263  exit(1);
 264}
 265
 266static char *
 267sbufalloc(struct strbuf *b, size_t n, size_t a)
 268{
 269  char *s;
 270
 271  if (n > b->cap - b->len) {
 272    if (n > SIZE_MAX - a || n + a > SIZE_MAX - b->len)
 273      fatal("path is too long");
 274    b->cap = ROUNDUP(n, a);
 275    s      = malloc(b->cap);
 276    if (!s)
 277      fatal(NULL);
 278    if (b->len)
 279      memcpy(s, b->str, b->len);
 280    free(b->str);
 281    b->str = s;
 282  }
 283  return b->str + b->len;
 284}
 285
 286static int
 287sbuffmtv(struct strbuf *b, size_t a, const char *fmt, va_list ap)
 288{
 289  va_list aptmp;
 290  int     n;
 291
 292  va_copy(aptmp, ap);
 293  n = vsnprintf(b->str ? b->str + b->len : NULL, b->cap - b->len, fmt, aptmp);
 294  va_end(aptmp);
 295  if (n < 0)
 296    fatal("vsnprintf:");
 297  if ((size_t)n >= b->cap - b->len) {
 298    sbufalloc(b, (size_t)n + 1, a);
 299    n = vsnprintf(b->str + b->len, b->cap - b->len, fmt, ap);
 300    if (n < 0)
 301      fatal("vsnprintf:");
 302    if ((size_t)n >= b->cap - b->len)
 303      fatal("vsnprintf: formatted size changed");
 304  }
 305  b->len += n;
 306  return n;
 307}
 308
 309static int
 310sbuffmt(struct strbuf *b, size_t a, const char *fmt, ...)
 311{
 312  va_list ap;
 313  int     n;
 314
 315  va_start(ap, fmt);
 316  n = sbuffmtv(b, a, fmt, ap);
 317  va_end(ap);
 318  return n;
 319}
 320
 321static void
 322sbufcat(struct strbuf *b, const char *s, size_t n, size_t a)
 323{
 324  char *d;
 325
 326  d = sbufalloc(b, n + 1, a);
 327  memcpy(d, s, n);
 328  d[n] = 0;
 329  b->len += n;
 330}
 331
 332static void
 333bioinit(struct bufio *f, int fd)
 334{
 335  f->fd  = fd;
 336  f->pos = f->end = f->buf;
 337  f->off          = 0;
 338}
 339
 340static size_t
 341bioread(struct bufio *f, void *p, size_t n)
 342{
 343  size_t         l;
 344  unsigned char *d;
 345  struct iovec   iov[2];
 346  ssize_t        r;
 347
 348  d = p;
 349  if (f->pos != f->end) {
 350    l = f->end - f->pos;
 351    if (n < l)
 352      l = n;
 353    memcpy(d, f->pos, l);
 354    f->pos += l;
 355    n -= l;
 356    d += l;
 357  }
 358  iov[1].iov_base = f->buf;
 359  iov[1].iov_len  = sizeof f->buf;
 360  for (; n > 0; n -= r, d += r) {
 361    iov[0].iov_base = d;
 362    iov[0].iov_len  = n;
 363    r               = readv(f->fd, iov, 2);
 364    if (r < 0)
 365      f->err = errno;
 366    if (r <= 0)
 367      break;
 368    if ((size_t)r >= n) {
 369      f->pos = f->buf;
 370      f->end = f->buf + (r - n);
 371      r      = n;
 372    }
 373  }
 374  l = d - (unsigned char *)p;
 375  f->off += l;
 376  return l;
 377}
 378
 379static int
 380bioskip(struct bufio *f, off_t n)
 381{
 382  static int seekfail;
 383  size_t     l;
 384  ssize_t    r;
 385
 386  if (f->pos != f->end) {
 387    l = f->end - f->pos;
 388    if (n < (off_t)l) {
 389      f->pos += n;
 390      return 0;
 391    }
 392    n -= l;
 393    f->pos = f->end = f->buf;
 394  }
 395  if (!seekfail) {
 396    if (n == 0 || lseek(f->fd, n, SEEK_CUR) >= 0)
 397      return 0;
 398    seekfail = 1;
 399  }
 400  for (; n > 0; n -= r) {
 401    l = sizeof f->buf;
 402    if (n < (off_t)l)
 403      l = n;
 404    r = read(f->fd, f->buf, l);
 405    if (r <= 0)
 406      return -1;
 407  }
 408  return 0;
 409}
 410
 411static void
 412copyblock(char *b, struct bufio *r, size_t nr, FILE *w, size_t nw)
 413{
 414  if (bioread(r, b, nr) != nr) {
 415    if (r->err)
 416      fatal("read: %s", strerror(r->err));
 417    fatal("archive truncated");
 418  }
 419  if (nw > nr)
 420    memset(b + nr, 0, nw - nr);
 421  if (nw && fwrite(b, 1, nw, w) != nw)
 422    fatal("write:");
 423}
 424
 425/* nr and nw must differ by at most 8192 */
 426static void
 427copy(struct bufio *r, off_t nr, FILE *w, off_t nw)
 428{
 429  char b[8192];
 430
 431  assert(nr - nw <= (off_t)sizeof b || nw - nr <= (off_t)sizeof b);
 432  for (; nr > (off_t)sizeof b && nw > (off_t)sizeof b; nr -= (off_t)sizeof b, nw -= (off_t)sizeof b)
 433    copyblock(b, r, sizeof b, w, sizeof b);
 434  copyblock(b, r, nr, w, nw);
 435}
 436
 437static struct account *
 438findaccount(const char *name, uid_t uid, gid_t gid)
 439{
 440  static struct account *accts;
 441  static size_t          acctslen;
 442  struct account        *a;
 443
 444  for (a = accts; a < accts + acctslen; ++a) {
 445    if ((uid == (uid_t)-1 || uid == a->uid) && (gid == (gid_t)-1 || gid == a->gid)
 446        && (!name || (a->name && strcmp(a->name, name) == 0)))
 447      return a;
 448  }
 449  if ((acctslen & (acctslen - 1)) == 0) {
 450    accts = reallocarray(accts, acctslen ? acctslen * 2 : 16, sizeof *accts);
 451    if (!accts)
 452      fatal(NULL);
 453  }
 454  a       = &accts[acctslen++];
 455  a->name = NULL;
 456  a->type = 0;
 457  a->uid  = -1;
 458  a->gid  = -1;
 459  if (name) {
 460    a->name = strdup(name);
 461    if (!a->name)
 462      fatal(NULL);
 463  }
 464  return a;
 465}
 466
 467static uid_t
 468unametouid(const char *uname, uid_t fallback)
 469{
 470  struct account *a;
 471  struct passwd  *pw;
 472
 473  if (!*uname)
 474    return fallback;
 475  a = findaccount(uname, (uid_t)-1, (gid_t)-1);
 476  if (~a->type & UID) {
 477    a->type |= UID;
 478    pw     = getpwnam(uname);
 479    a->uid = pw ? pw->pw_uid : (uid_t)-1;
 480  }
 481  return a->uid != (uid_t)-1 ? a->uid : fallback;
 482}
 483
 484static gid_t
 485gnametogid(const char *gname, gid_t fallback)
 486{
 487  struct account *a;
 488  struct group   *gr;
 489
 490  if (!*gname)
 491    return fallback;
 492  a = findaccount(gname, (uid_t)-1, (gid_t)-1);
 493  if (~a->type & GID) {
 494    a->type |= GID;
 495    gr     = getgrnam(gname);
 496    a->gid = gr ? gr->gr_gid : (gid_t)-1;
 497  }
 498  return a->gid != (gid_t)-1 ? a->gid : fallback;
 499}
 500
 501static char *
 502uidtouname(uid_t uid, char *fallback)
 503{
 504  struct account *a;
 505  struct passwd  *pw;
 506
 507  a = findaccount(NULL, uid, (gid_t)-1);
 508  if (~a->type & UID) {
 509    a->type |= UID;
 510    a->uid = uid;
 511    assert(!a->name);
 512    pw = getpwuid(uid);
 513    if (pw) {
 514      a->name = strdup(pw->pw_name);
 515      if (!a->name)
 516        fatal(NULL);
 517    }
 518  }
 519  return a->name ? a->name : fallback;
 520}
 521
 522static char *
 523gidtogname(gid_t gid, char *fallback)
 524{
 525  struct account *a;
 526  struct group   *gr;
 527
 528  a = findaccount(NULL, (uid_t)-1, gid);
 529  if (~a->type & GID) {
 530    a->type |= GID;
 531    a->gid = gid;
 532    assert(!a->name);
 533    gr = getgrgid(gid);
 534    if (gr) {
 535      a->name = strdup(gr->gr_name);
 536      if (!a->name)
 537        fatal(NULL);
 538    }
 539  }
 540  return a->name ? a->name : fallback;
 541}
 542
 543static unsigned long long
 544octnum(const char *str, size_t len)
 545{
 546  const char        *end;
 547  unsigned           c;
 548  unsigned long long n;
 549
 550  n   = 0;
 551  end = str + len;
 552  /* some archives have leading spaces, so skip them */
 553  for (; str != end && *str == ' '; ++str)
 554    ;
 555  for (; str != end; ++str) {
 556    c = *str;
 557    if (c == ' ' || c == '\0')
 558      break;
 559    c -= '0';
 560    if (c > 7)
 561      fatal("invalid number field");
 562    n = n * 8 + c;
 563  }
 564  return n;
 565}
 566
 567static unsigned long long
 568decnum(const char *str, size_t len, char **pos)
 569{
 570  const char        *end;
 571  unsigned           c;
 572  unsigned long long n;
 573
 574  n   = 0;
 575  end = str + len;
 576  for (; str != end; ++str) {
 577    c = *str - '0';
 578    if (c > 9)
 579      break;
 580    n = n * 10 + c;
 581  }
 582  if (pos)
 583    *pos = (char *)str;
 584  return n;
 585}
 586
 587static int
 588readustar(struct bufio *f, struct header *h)
 589{
 590  static char   buf[512];
 591  static off_t  end;
 592  size_t        namelen, prefixlen, linklen;
 593  unsigned long sum;
 594  int           i;
 595  enum format   format;
 596
 597  assert(bioin.off <= end);
 598  if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) {
 599    if (f->err)
 600      fatal("read: %s", strerror(f->err));
 601    fatal("archive truncated");
 602  }
 603  sum = 0;
 604  for (i = 0; i < 512; ++i)
 605    sum += ((unsigned char *)buf)[i];
 606  if (sum == 0)
 607    return 0;
 608  for (i = 148; i < 156; ++i)
 609    sum += ' ' - ((unsigned char *)buf)[i];
 610  if (sum != octnum(buf + 148, 8))
 611    fatal("invalid tar header: bad checksum");
 612  if (memcmp(
 613          buf + 257,
 614          "ustar\0"
 615          "00",
 616          8
 617      )
 618      == 0)
 619    format = USTAR;
 620  else if (memcmp(buf + 257, "ustar  ", 8) == 0)
 621    format = GNUTAR;
 622  else
 623    format = V7;
 624  h->fields = PATH | UID | GID | SIZE;
 625  namelen   = strnlen(buf, 100);
 626  prefixlen = format == USTAR ? strnlen(buf + 345, 155) : 0;
 627  if (namelen == 100 || prefixlen > 0) {
 628    h->pathbuf.len = 0;
 629    if (prefixlen > 0) {
 630      sbufcat(&h->pathbuf, buf + 345, prefixlen, 1024);
 631      sbufcat(&h->pathbuf, "/", 1, 1024);
 632    }
 633    sbufcat(&h->pathbuf, buf, namelen, 1024);
 634    h->path    = h->pathbuf.str;
 635    h->pathlen = h->pathbuf.len;
 636  } else {
 637    h->path    = buf;
 638    h->pathlen = namelen;
 639  }
 640  h->dev   = 0;
 641  h->ino   = 0;
 642  h->mode  = octnum(buf + 100, 8);
 643  h->uid   = octnum(buf + 108, 8);
 644  h->gid   = octnum(buf + 116, 8);
 645  h->nlink = 1;
 646  h->size  = octnum(buf + 124, 12);
 647  end      = bioin.off + ROUNDUP(h->size, 512);
 648  h->mtime = (struct timespec){.tv_sec = octnum(buf + 136, 12)};
 649  if (format == GNUTAR) {
 650    h->fields |= ATIME | CTIME;
 651    h->atime = (struct timespec){.tv_sec = octnum(buf + 345, 12)};
 652    h->ctime = (struct timespec){.tv_sec = octnum(buf + 357, 12)};
 653  }
 654  h->type = buf[156];
 655  if (h->type == AREGTYPE)
 656    h->type = REGTYPE;
 657
 658  linklen = strnlen(buf + 157, 100);
 659  if (linklen > 0)
 660    h->fields |= LINKPATH;
 661  if (linklen == 100) {
 662    h->linkbuf.len = 0;
 663    sbufcat(&h->linkbuf, buf + 157, 100, 1024);
 664    h->link    = h->linkbuf.str;
 665    h->linklen = h->linkbuf.len;
 666  } else {
 667    h->link = buf + 157;
 668  }
 669  h->linklen = linklen;
 670  if (format == V7) {
 671    h->uname = "";
 672    h->gname = "";
 673  } else {
 674    h->fields |= UNAME | GNAME;
 675    h->uname = buf + 265;
 676    if (!memchr(h->uname, '\0', 32))
 677      fatal("uname is not NUL-terminated");
 678    h->gname = buf + 297;
 679    if (!memchr(h->gname, '\0', 32))
 680      fatal("gname is not NUL-terminated");
 681    if (h->type == CHRTYPE || h->type == BLKTYPE) {
 682      unsigned major, minor;
 683
 684      major   = octnum(buf + 329, 8);
 685      minor   = octnum(buf + 337, 8);
 686      h->rdev = makedev(major, minor);
 687    }
 688  }
 689  return 1;
 690}
 691
 692static void
 693parsetime(struct timespec *ts, const char *field, const char *str, size_t len)
 694{
 695  const char        *end = str + len;
 696  char              *pos;
 697  unsigned long long subsec;
 698  size_t             sublen;
 699
 700  ts->tv_sec = decnum(str, len, &pos);
 701  if (*pos == '.') {
 702    str    = ++pos;
 703    subsec = decnum(str, end - str, &pos);
 704    for (sublen = pos - str; sublen < 9; ++sublen)
 705      subsec *= 10;
 706    ts->tv_nsec = subsec % 1000000000;
 707  }
 708  if (pos != end)
 709    fatal("invalid extended header: bad %s", field);
 710}
 711
 712static void
 713extkeyval(struct header *h, const char *key, const char *val, size_t vallen)
 714{
 715  enum field            field;
 716  char                 *end;
 717  const struct keyword *kw;
 718
 719  field = 0;
 720  for (kw = keywords; kw != keywords + LEN(keywords); ++kw) {
 721    if (strcmp(key, kw->name) == 0) {
 722      field = kw->field;
 723      break;
 724    }
 725  }
 726  if (!field) {
 727    if (strcmp(key, "charset") == 0) {
 728    } else if (strcmp(key, "comment") == 0) {
 729      /* ignore */
 730    } else if (strcmp(key, "hdrcharset") == 0) {
 731    } else if (strncmp(key, "realtime.", 9) == 0) {
 732    } else if (strncmp(key, "security.", 9) == 0) {
 733    } else {
 734      fprintf(stderr, "ignoring unknown keyword '%s'\n", key);
 735    }
 736    return;
 737  }
 738  if ((h->delete | opt.delete) & field)
 739    return;
 740
 741  switch (field) {
 742    case ATIME:
 743      parsetime(&h->atime, "atime", val, vallen);
 744      break;
 745    case CTIME:
 746      parsetime(&h->ctime, "ctime", val, vallen);
 747      break;
 748    case GID:
 749      h->gid = decnum(val, vallen, &end);
 750      if (end != val + vallen)
 751        fatal("invalid extended header: bad gid");
 752      break;
 753    case GNAME:
 754      h->gnamebuf.len = 0;
 755      sbufcat(&h->gnamebuf, val, vallen, 256);
 756      h->gname = h->gnamebuf.str;
 757      break;
 758    case LINKPATH:
 759      h->linkbuf.len = 0;
 760      sbufcat(&h->linkbuf, val, vallen, 1024);
 761      h->link    = h->linkbuf.str;
 762      h->linklen = h->linkbuf.len;
 763      break;
 764    case MTIME:
 765      parsetime(&h->mtime, "mtime", val, vallen);
 766      break;
 767    case PATH:
 768      h->pathbuf.len = 0;
 769      sbufcat(&h->pathbuf, val, vallen, 1024);
 770      h->path    = h->pathbuf.str;
 771      h->pathlen = h->pathbuf.len;
 772      break;
 773    case SIZE:
 774      h->size = decnum(val, vallen, &end);
 775      if (end != val + vallen)
 776        fatal("invalid extended header: bad size");
 777      break;
 778    case UID:
 779      h->uid = decnum(val, vallen, &end);
 780      if (end != val + vallen)
 781        fatal("invalid extended header: bad uid");
 782      break;
 783    case UNAME:
 784      h->unamebuf.len = 0;
 785      sbufcat(&h->unamebuf, val, vallen, 256);
 786      h->uname = h->unamebuf.str;
 787      break;
 788    default:
 789      return;
 790  }
 791  h->fields |= field;
 792}
 793
 794static void
 795readexthdr(struct bufio *f, struct header *h, off_t len)
 796{
 797  static struct strbuf buf;
 798  size_t               reclen, vallen;
 799  char                *rec, *end, *key, *val;
 800
 801  if (len > (off_t)SIZE_MAX)
 802    fatal("extended header is too large");
 803  buf.len = 0;
 804  sbufalloc(&buf, (size_t)len, 8192);
 805  if (bioread(f, buf.str, (size_t)len) != (size_t)len) {
 806    if (f->err)
 807      fatal("read: %s", strerror(f->err));
 808    fatal("archive truncated");
 809  }
 810  rec = buf.str;
 811  while (len > 0) {
 812    end = memchr(rec, '\n', len);
 813    if (!end)
 814      fatal(
 815          "invalid extended header: record is missing "
 816          "newline"
 817      );
 818    *end   = '\0';
 819    reclen = decnum(rec, (size_t)(end - rec), &key);
 820    if (*key != ' ' || reclen != (unsigned long long)(end - rec + 1))
 821      fatal("invalid extended header: invalid record");
 822    ++key;
 823    val = strchr(key, '=');
 824    if (!val)
 825      fatal("invalid extended header: record has no '='");
 826    *val++ = '\0';
 827    vallen = end - val;
 828    extkeyval(h, key, val, vallen);
 829    len -= reclen;
 830    rec += reclen;
 831  }
 832}
 833
 834static void
 835readgnuhdr(struct bufio *f, struct strbuf *b, off_t len)
 836{
 837  if (len > (off_t)(SIZE_MAX - 1))
 838    fatal("GNU header is too large");
 839  b->len = 0;
 840  sbufalloc(b, (size_t)(len + 1), 1024);
 841  if (bioread(f, b->str, (size_t)len) != (size_t)len) {
 842    if (f->err)
 843      fatal("read: %s", strerror(f->err));
 844    fatal("archive truncated");
 845  }
 846  b->str[len] = '\0';
 847  b->len      = len;
 848}
 849
 850static int
 851readpax(struct bufio *f, struct header *h)
 852{
 853  exthdr.fields = exthdr.delete;
 854  while (readustar(f, h)) {
 855    switch (h->type) {
 856      case 'g':
 857        readexthdr(f, &globexthdr, h->size);
 858        break;
 859        // ?man -x: hex format or match whole lines
 860      case 'x':
 861        readexthdr(f, &exthdr, h->size);
 862        break;
 863        // ?man -L: specify option flag
 864      case 'L':
 865        if ((exthdr.delete | opt.delete) & PATH)
 866          break;
 867        readgnuhdr(f, &exthdr.pathbuf, h->size);
 868        exthdr.path    = exthdr.pathbuf.str;
 869        exthdr.pathlen = exthdr.pathbuf.len;
 870        exthdr.fields |= PATH;
 871        break;
 872      case 'K':
 873        if ((exthdr.delete | opt.delete) & LINKPATH)
 874          break;
 875        readgnuhdr(f, &exthdr.linkbuf, h->size);
 876        exthdr.link    = exthdr.linkbuf.str;
 877        exthdr.linklen = exthdr.linkbuf.len;
 878        exthdr.fields |= LINKPATH;
 879        break;
 880      default:
 881        return 1;
 882    }
 883  }
 884  return 0;
 885}
 886
 887static int
 888readcpio(struct bufio *f, struct header *h)
 889{
 890  static off_t  end;
 891  unsigned long type;
 892  char          buf[76];
 893
 894  if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) {
 895    if (f->err)
 896      fatal("read: %s", strerror(f->err));
 897    fatal("archive truncated");
 898  }
 899  if (memcmp(buf, "070707", 6) != 0)
 900    fatal("invalid cpio header: bad magic");
 901  h->pathlen = octnum(buf + 59, 6);
 902  if (h->pathlen == 0)
 903    fatal("invalid cpio header: c_namesize is 0");
 904  h->pathbuf.len = 0;
 905  sbufalloc(&h->pathbuf, h->pathlen, 1024);
 906  h->path = h->pathbuf.str;
 907  if (bioread(f, h->path, h->pathlen) != h->pathlen) {
 908    if (f->err)
 909      fatal("read: %s", strerror(f->err));
 910    fatal("archive truncated");
 911  }
 912  if (h->path[--h->pathlen] != '\0')
 913    fatal("invalid cpio header: name is not NUL-terminated");
 914  if (strcmp(h->path, "TRAILER!!!") == 0)
 915    return 0;
 916
 917  h->fields = PATH | MODE | UID | GID | MTIME | SIZE;
 918  h->dev    = octnum(buf + 6, 6);
 919  h->ino    = octnum(buf + 12, 6);
 920  type      = octnum(buf + 18, 6);
 921  h->mode   = type & 07777;
 922  type &= ~07777;
 923  switch (type) {
 924    case C_ISDIR:
 925      h->type = DIRTYPE;
 926      break;
 927    case C_ISFIFO:
 928      h->type = FIFOTYPE;
 929      break;
 930    case C_ISREG:
 931      h->type = REGTYPE;
 932      break;
 933    case C_ISLNK:
 934      h->type = SYMTYPE;
 935      break;
 936    case C_ISBLK:
 937      h->type = BLKTYPE;
 938      break;
 939    case C_ISCHR:
 940      h->type = CHRTYPE;
 941      break;
 942    default:
 943      fatal(
 944          "invalid cpio header: invalid or unsupported file type: "
 945          "%#o",
 946          type
 947      );
 948  }
 949  h->uid   = octnum(buf + 24, 6);
 950  h->gid   = octnum(buf + 30, 6);
 951  h->nlink = octnum(buf + 36, 6);
 952  h->rdev  = octnum(buf + 42, 6);
 953  h->mtime = (struct timespec){.tv_sec = octnum(buf + 48, 11)};
 954  h->size  = octnum(buf + 65, 11);
 955  h->uname = "";
 956  h->gname = "";
 957  if (h->type == SYMTYPE) {
 958    if (h->size > (off_t)(SIZE_MAX - 1))
 959      fatal("symlink target is too long");
 960    h->linklen     = h->size;
 961    h->linkbuf.len = 0;
 962    h->link        = sbufalloc(&h->linkbuf, h->linklen + 1, 1024);
 963    if (bioread(f, h->link, h->linklen) != h->linklen) {
 964      if (f->err)
 965        fatal("read: %s", strerror(f->err));
 966      fatal("archive truncated");
 967    }
 968    h->link[h->linklen] = '\0';
 969    h->size             = 0;
 970    h->fields |= LINKPATH;
 971  } else {
 972    h->link    = "";
 973    h->linklen = 0;
 974  }
 975  end = bioin.off + h->size;
 976  return 1;
 977}
 978
 979static int
 980decompress(const char *algo, int fd, pid_t *pid)
 981{
 982  extern char              **environ;
 983  posix_spawn_file_actions_t fa;
 984  int                        p[2], err;
 985  char                      *argv[3];
 986
 987  if (!algo)
 988    return fd;
 989  argv[0] = (char *)algo;
 990  argv[1] = "-dc";
 991  argv[2] = NULL;
 992  if (pipe2(p, O_CLOEXEC) != 0)
 993    fatal("pipe2:");
 994  err = posix_spawn_file_actions_init(&fa);
 995  if (err)
 996    fatal("posix_spawn_file_actions_init: %s", strerror(errno));
 997  err = posix_spawn_file_actions_adddup2(&fa, fd, 0);
 998  if (err)
 999    fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1000  err = posix_spawn_file_actions_adddup2(&fa, p[1], 1);
1001  if (err)
1002    fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1003  err = posix_spawnp(pid, algo, &fa, NULL, argv, environ);
1004  if (err)
1005    fatal("posix_spawnp %s: %s", algo, strerror(errno));
1006  close(p[1]);
1007  return p[0];
1008}
1009
1010static readfn *
1011detectformat(struct bufio *f, const char *algo, pid_t *pid)
1012{
1013  size_t         l, i;
1014  ssize_t        n;
1015  unsigned char *b;
1016
1017again:
1018  f->fd = decompress(algo, f->fd, pid);
1019  b     = (unsigned char *)f->buf;
1020  for (l = 0; l < 512; l += n) {
1021    n = read(f->fd, b + l, 512 - l);
1022    if (n < 0)
1023      fatal("read:");
1024    if (n == 0)
1025      break;
1026  }
1027  f->pos = f->buf;
1028  f->end = f->buf + l;
1029  if (l == 512) {
1030    unsigned long sum, hdrsum;
1031
1032    sum = 0;
1033    for (i = 0; i < 512; ++i)
1034      sum += b[i];
1035    if (sum == 0)
1036      return readpax;
1037    hdrsum = 0;
1038    for (i = 148; i < 156; ++i) {
1039      sum += ' ' - b[i];
1040      if (b[i] >= '0' && b[i] <= '9')
1041        hdrsum = hdrsum * 8 + (b[i] - '0');
1042    }
1043    if (sum == hdrsum)
1044      return readpax;
1045  }
1046  if (l >= 76) {
1047    if (memcmp(b, "070707", 6) == 0)
1048      return readcpio;
1049  }
1050  if (!algo) {
1051    static const struct command {
1052      char          algo[6];
1053      unsigned char magiclen;
1054      unsigned char magic[6];
1055    } cmds[] = {
1056        {"gzip", 2, {0x1F, 0x8B}},
1057        {"bzip2", 2, {'B', 'Z'}},
1058        {"xz", 6, {0xFD, '7', 'z', 'X', 'Z', 0x00}},
1059        {"zstd", 4, {0x28, 0xB5, 0x2F, 0xFD}},
1060        {"lzip", 4, {'L', 'Z', 'I', 'P'}},
1061    };
1062    const struct command *c;
1063
1064    for (c = cmds; c < cmds + LEN(cmds); ++c) {
1065      if (l >= c->magiclen && memcmp(b, c->magic, c->magiclen) == 0) {
1066        if (lseek(f->fd, 0, SEEK_SET) != 0)
1067          fatal(
1068              "compression detection requires "
1069              "seekable input"
1070          );
1071        algo = c->algo;
1072        goto again;
1073      }
1074    }
1075  }
1076  return NULL;
1077}
1078
1079static FILE *
1080compress(const char *algo, const char *name, pid_t *pid)
1081{
1082  extern char              **environ;
1083  FILE                      *f;
1084  int                        fd, p[2], err;
1085  posix_spawn_file_actions_t fa;
1086  char                      *argv[3];
1087
1088  if (!algo) {
1089    if (name && !freopen(name, aflag ? "r+" : "w", stdout))
1090      fatal("open %s:");
1091    if (aflag && name) {
1092      if (fseek(stdout, 0, SEEK_END) != 0)
1093        fatal("fseek %s:");
1094    }
1095    return stdout;
1096  }
1097  argv[0] = (char *)algo;
1098  argv[1] = "-c";
1099  argv[2] = NULL;
1100  if (name) {
1101    fd = open(name, O_WRONLY | O_CREAT, 0666);
1102    if (fd < 0)
1103      fatal("open %s:");
1104  } else {
1105    fd = 1;
1106  }
1107  if (pipe2(p, O_CLOEXEC) != 0)
1108    fatal("pipe2:");
1109  f = fdopen(p[1], "w");
1110  if (!f)
1111    fatal("fdopen:");
1112  err = posix_spawn_file_actions_init(&fa);
1113  if (err)
1114    fatal("posix_spawn_file_actions_init: %s", strerror(errno));
1115  err = posix_spawn_file_actions_adddup2(&fa, p[0], 0);
1116  if (err)
1117    fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1118  err = posix_spawn_file_actions_adddup2(&fa, fd, 1);
1119  if (err)
1120    fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1121  err = posix_spawnp(pid, algo, &fa, NULL, argv, environ);
1122  if (err)
1123    fatal("posix_spawnp %s: %s", algo, strerror(errno));
1124  close(fd);
1125  close(p[0]);
1126  return f;
1127}
1128
1129static char *
1130splitname(char *name, size_t namelen)
1131{
1132  char *slash;
1133
1134  if (namelen > 256)
1135    return NULL;
1136  slash = memchr(name + namelen - 100, '/', 100);
1137  if (!slash || slash - name > 155)
1138    return NULL;
1139  return slash;
1140}
1141
1142static void
1143openfile(struct header *h)
1144{
1145  int fd;
1146
1147  if (h->file) {
1148    fd = open(h->file, O_RDONLY);
1149    if (fd < 0)
1150      fatal("open %s:", h->file);
1151    bioinit(&bioin, fd);
1152  }
1153}
1154
1155static void
1156closefile(struct header *h)
1157{
1158  if (h->file) {
1159    if (tflag)
1160      futimens(bioin.fd, (struct timespec[2]){h->fileatime, {.tv_nsec = UTIME_OMIT}});
1161    close(bioin.fd);
1162  }
1163}
1164
1165static void
1166closeustar(FILE *f)
1167{
1168  char pad[512];
1169
1170  memset(pad, 0, 512);
1171  if (fwrite(pad, 512, 1, f) != 1)
1172    fatal("write:");
1173  if (fwrite(pad, 512, 1, f) != 1)
1174    fatal("write:");
1175}
1176
1177static void
1178writeustar(FILE *f, struct header *h)
1179{
1180  char          buf[512], *slash, tmp[32];
1181  unsigned long sum;
1182  int           i;
1183
1184  if (!h) {
1185    closeustar(f);
1186    return;
1187  }
1188  slash = h->slash;
1189  if (!slash && h->pathlen > 100) {
1190    slash = splitname(h->path, h->pathlen);
1191    if (!slash)
1192      fatal("path is too long: %s\n", h->path);
1193  }
1194  if (slash) {
1195    size_t len;
1196
1197    strncpy(buf, slash + 1, 100);
1198    len = slash - h->path;
1199    memcpy(buf + 345, h->path, len);
1200    memset(buf + 345 + len, 0, 155 - len);
1201  } else {
1202    strncpy(buf, h->path, 100);
1203    memset(buf + 345, 0, 155);
1204  }
1205  if (h->mode > 07777777)
1206    fatal("mode is too large: %ju", (uintmax_t)h->mode);
1207  snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->mode & 07777777);
1208  memcpy(buf + 100, tmp, 8);
1209  if (h->uid > MAXUGID)
1210    fatal("uid is too large: %ju", (uintmax_t)h->uid);
1211  snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->uid & 07777777);
1212  memcpy(buf + 108, tmp, 8);
1213  if (h->gid > MAXUGID)
1214    fatal("gid is too large: %ju", (uintmax_t)h->gid);
1215  snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)h->gid & 07777777);
1216  memcpy(buf + 116, tmp, 8);
1217  if (h->size < 0 || h->size > MAXSIZE)
1218    fatal("size is too large: %ju", (uintmax_t)h->size);
1219  snprintf(buf + 124, 12, "%.11llo", (unsigned long long)h->size);
1220  if (h->mtime.tv_sec < 0 || h->mtime.tv_sec > MAXTIME)
1221    fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec);
1222  snprintf(buf + 136, 12, "%.11llo", (unsigned long long)h->mtime.tv_sec);
1223  memset(buf + 148, ' ', 8);
1224  buf[156] = h->type;
1225  if (h->linklen > 100)
1226    fatal("link name is too long: %s\n", h->link);
1227  strncpy(buf + 157, h->link, 100);
1228  memcpy(buf + 257, "ustar", 6);
1229  memcpy(buf + 263, "00", 2);
1230  if (strlen(h->uname) > 31)
1231    fatal("user name is too long: %s\n", h->uname);
1232  strncpy(buf + 265, h->uname, 32);
1233  if (strlen(h->gname) > 31)
1234    fatal("group name is too long: %s\n", h->gname);
1235  strncpy(buf + 297, h->gname, 32);
1236  if (major(h->rdev) > 07777777)
1237    fatal("device major is too large: %ju\n", (uintmax_t)major(h->rdev));
1238  snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)major(h->rdev) & 07777777);
1239  memcpy(buf + 329, tmp, 8);
1240  if (minor(h->rdev) > 07777777)
1241    fatal("device minor is too large: %ju\n", (uintmax_t)minor(h->rdev));
1242  snprintf(tmp, sizeof(tmp), "%.7lo", (unsigned long)minor(h->rdev) & 07777777);
1243  memcpy(buf + 337, tmp, 8);
1244  memset(buf + 500, 0, 12);
1245  sum = 0;
1246  for (i = 0; i < 512; ++i)
1247    sum += ((unsigned char *)buf)[i];
1248  snprintf(tmp, sizeof(tmp), "%.7lo", sum & 07777777);
1249  memcpy(buf + 148, tmp, 8);
1250  if (fwrite(buf, 512, 1, f) != 1)
1251    fatal("write:");
1252  if (h->data) {
1253    size_t pad;
1254
1255    if (fwrite(h->data, 1, (size_t)h->size, f) != (size_t)h->size)
1256      fatal("write:");
1257    pad = (size_t)(ROUNDUP(h->size, 512) - h->size);
1258    memset(bioin.buf, 0, pad);
1259    if (fwrite(bioin.buf, 1, pad, f) != pad)
1260      fatal("write:");
1261  } else if (h->size > 0) {
1262    openfile(h);
1263    copy(&bioin, h->size, f, ROUNDUP(h->size, 512));
1264    closefile(h);
1265  }
1266}
1267
1268static void
1269writerec(struct strbuf *ext, const char *fmt, ...)
1270{
1271  static struct strbuf buf;
1272  va_list              ap;
1273  int                  d, n, m, l;
1274
1275  buf.len = 0;
1276  va_start(ap, fmt);
1277  l = sbuffmtv(&buf, 256, fmt, ap);
1278  va_end(ap);
1279
1280  d = 0;
1281  m = 1;
1282  for (n = l; n > 0; n /= 10) {
1283    m *= 10;
1284    ++d;
1285  }
1286  n = d + 1 + l + 1;
1287  if (n >= m)
1288    ++n;
1289  sbuffmt(ext, 256, "%d %.*s\n", n, l, buf.str);
1290}
1291
1292static void
1293writetimerec(struct strbuf *ext, char *kw, struct timespec *ts)
1294{
1295  if (ts->tv_nsec != 0)
1296    writerec(ext, "%s=%ju.%.9ld", kw, (uintmax_t)ts->tv_sec, ts->tv_nsec % 1000000000);
1297  else
1298    writerec(ext, "%s=%ju", kw, (uintmax_t)ts->tv_sec);
1299}
1300
1301static void
1302writeexthdr(FILE *f, int type, struct header *h)
1303{
1304  static struct strbuf ext;
1305  struct header        exthdr;
1306
1307  ext.len = 0;
1308  if (h->fields & PATH)
1309    writerec(&ext, "path=%s", h->path);
1310  if (h->fields & UID)
1311    writerec(&ext, "uid=%ju", (uintmax_t)h->uid);
1312  if (h->fields & GID)
1313    writerec(&ext, "gid=%ju", (uintmax_t)h->gid);
1314  if (h->fields & SIZE)
1315    writerec(&ext, "size=%ju", (uintmax_t)h->size);
1316  if (h->fields & MTIME)
1317    writetimerec(&ext, "mtime", &h->mtime);
1318  if (h->fields & ATIME)
1319    writetimerec(&ext, "atime", &h->atime);
1320  if (h->fields & CTIME)
1321    writetimerec(&ext, "ctime", &h->ctime);
1322  if (h->fields & UNAME)
1323    writerec(&ext, "uname=%s", h->uname);
1324  if (h->fields & GNAME)
1325    writerec(&ext, "gname=%s", h->gname);
1326  if (ext.len > 0) {
1327    memset(&exthdr, 0, sizeof exthdr);
1328    exthdr.path    = "pax_extended_header";
1329    exthdr.pathlen = 20;
1330    exthdr.mode    = 0600;
1331    exthdr.link    = "";
1332    exthdr.uname   = "";
1333    exthdr.gname   = "";
1334    exthdr.size    = ext.len;
1335    exthdr.type    = type;
1336    exthdr.data    = ext.str;
1337    writeustar(f, &exthdr);
1338  }
1339}
1340
1341static void
1342mergehdr(struct header *dst, struct header *src, enum field fields)
1343{
1344  fields &= src->fields;
1345  if (fields & PATH) {
1346    dst->path    = src->path;
1347    dst->pathlen = src->pathlen;
1348  }
1349  if (fields & UID)
1350    dst->uid = src->uid;
1351  if (fields & GID)
1352    dst->gid = src->gid;
1353  if (fields & SIZE)
1354    dst->size = src->size;
1355  if (fields & MTIME)
1356    dst->mtime = src->mtime;
1357  if (fields & ATIME)
1358    dst->atime = src->atime;
1359  if (fields & CTIME)
1360    dst->ctime = src->ctime;
1361  if (fields & UNAME)
1362    dst->uname = src->uname;
1363  if (fields & GNAME)
1364    dst->gname = src->gname;
1365  if (fields & LINKPATH) {
1366    dst->link    = src->link;
1367    dst->linklen = src->linklen;
1368  }
1369  dst->fields |= fields;
1370}
1371
1372static void
1373writepax(FILE *f, struct header *h)
1374{
1375  enum field fields;
1376
1377  if (!h) {
1378    closeustar(f);
1379    return;
1380  }
1381  if (vflag)
1382    fprintf(stderr, "%s\n", h->path);
1383  fields = 0;
1384  if (h->pathlen > 100) {
1385    h->slash = splitname(h->path, h->pathlen);
1386    if (!h->slash)
1387      fields |= PATH;
1388  }
1389  if (h->uid > MAXUGID)
1390    fields |= UID;
1391  if (h->gid > MAXUGID)
1392    fields |= GID;
1393  if (h->size > MAXSIZE)
1394    fields |= SIZE;
1395  if (h->mtime.tv_sec > MAXTIME || h->mtime.tv_nsec != 0)
1396    fields |= MTIME;
1397  if (opt.times)
1398    fields |= ATIME | CTIME;
1399  if (strlen(h->uname) > 31)
1400    fields |= UNAME;
1401  if (strlen(h->gname) > 31)
1402    fields |= GNAME;
1403  if (h->linklen > 100)
1404    fields |= LINKPATH;
1405  fields &= ~(exthdr.fields | opt.delete);
1406  mergehdr(&exthdr, h, fields);
1407  writeexthdr(f, 'x', &exthdr);
1408
1409  /* reset fields merged into extended header */
1410  if (fields & PATH)
1411    h->path = "", h->pathlen = 0;
1412  if (fields & UID)
1413    h->uid = 0;
1414  if (fields & GID)
1415    h->gid = 0;
1416  if (fields & SIZE)
1417    h->size = 0;
1418  if (fields & MTIME) {
1419    if (h->mtime.tv_sec > MAXTIME)
1420      h->mtime.tv_sec = MAXTIME;
1421    h->mtime.tv_nsec = 0;
1422  }
1423  if (fields & ATIME) {
1424    if (h->atime.tv_sec > MAXTIME)
1425      h->atime.tv_sec = MAXTIME;
1426    h->atime.tv_nsec = 0;
1427  }
1428  if (fields & CTIME) {
1429    if (h->ctime.tv_sec > MAXTIME)
1430      h->ctime.tv_sec = MAXTIME;
1431    h->ctime.tv_nsec = 0;
1432  }
1433  if (fields & UNAME)
1434    h->uname = "";
1435  if (fields & GNAME)
1436    h->gname = "";
1437  if (fields & LINKPATH)
1438    h->link = "", h->linklen = 0;
1439  h->fields &= ~fields;
1440  writeustar(f, h);
1441}
1442
1443static void
1444writecpio(FILE *f, struct header *h)
1445{
1446  static unsigned long ino;
1447  char                 buf[77];
1448  unsigned long        mode;
1449  uintmax_t            size;
1450  size_t               namesize;
1451  int                  len;
1452
1453  if (!h) {
1454    memcpy(buf, "070707", 6);
1455    memset(buf + 6, '0', 70);
1456    memcpy(buf + 59, "000013", 6);
1457    if (fwrite(buf, 1, 76, f) != 76)
1458      fatal("write:");
1459    if (fwrite("TRAILER!!!", 1, 11, f) != 11)
1460      fatal("write:");
1461    return;
1462  }
1463  if (vflag)
1464    fprintf(stderr, "%s\n", h->path);
1465  mode = h->mode;
1466  switch (h->type) {
1467    case DIRTYPE:
1468      mode |= S_IFDIR;
1469      break;
1470    case FIFOTYPE:
1471      mode |= S_IFIFO;
1472      break;
1473    case REGTYPE:
1474      mode |= S_IFREG;
1475      break;
1476    case SYMTYPE:
1477      mode |= S_IFLNK;
1478      break;
1479    case BLKTYPE:
1480      mode |= S_IFBLK;
1481      break;
1482    case CHRTYPE:
1483      mode |= S_IFCHR;
1484      break;
1485    default:
1486      fatal("unknown or unsupported header type");
1487  }
1488  if (h->dev > 0777777)
1489    fatal("device is too large: %ju", (uintmax_t)h->dev);
1490  if (++ino > 0777777)
1491    fatal("inode is too large: %lu", ino);
1492  if (mode > 0777777)
1493    fatal("mode is too large: %lu", mode);
1494  if (h->uid > MAXUGID)
1495    fatal("uid is too large: %ju", (uintmax_t)h->uid);
1496  if (h->gid > MAXUGID)
1497    fatal("gid is too large: %ju", (uintmax_t)h->gid);
1498  if (h->nlink > 0777777)
1499    fatal("nlink is too large: %ju", (uintmax_t)h->nlink);
1500  if (h->rdev > 0777777)
1501    fatal("device is too large: %ju", (uintmax_t)h->rdev);
1502  if (h->mtime.tv_sec > MAXTIME)
1503    fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec);
1504  namesize = h->pathlen;
1505  if (namesize > 0 && h->path[namesize - 1] == '/')
1506    --namesize;
1507  if (namesize > 077777777777 - 1)
1508    fatal("path is too large: %ju", (uintmax_t)h->pathlen + 1);
1509  size = h->type == SYMTYPE ? (uintmax_t)h->linklen : (uintmax_t)h->size;
1510  if (size > MAXSIZE)
1511    fatal("size is too large: %ju", h->size);
1512  len = snprintf(
1513      buf,
1514      sizeof buf,
1515      "070707%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.11llo%.6lo%.11jo",
1516      (unsigned long)h->dev,
1517      ino,
1518      mode,
1519      (unsigned long)h->uid,
1520      (unsigned long)h->gid,
1521      (unsigned long)h->nlink,
1522      (unsigned long)h->rdev,
1523      (unsigned long long)h->mtime.tv_sec,
1524      (unsigned long)namesize + 1,
1525      size
1526  );
1527  assert(len == 76);
1528  if (fwrite(buf, 1, 76, f) != 76)
1529    fatal("write:");
1530  if (fwrite(h->path, 1, namesize, f) != namesize || fputc('\0', f) == EOF)
1531    fatal("write:");
1532  switch (h->type) {
1533    case SYMTYPE:
1534      if (fwrite(h->link, 1, h->linklen, f) != h->linklen)
1535        fatal("write:");
1536      break;
1537    case REGTYPE:
1538      openfile(h);
1539      copy(&bioin, h->size, f, h->size);
1540      closefile(h);
1541      break;
1542    default:
1543      break;
1544  }
1545}
1546
1547static void
1548filepush(struct filelist *files, const char *name, size_t pathlen, dev_t dev)
1549{
1550  struct file *f;
1551  size_t       namelen;
1552
1553  namelen = strlen(name);
1554  f       = malloc(sizeof *f + namelen + 1);
1555  if (!f)
1556    fatal(NULL);
1557  memcpy(f->name, name, namelen + 1);
1558  f->namelen     = namelen;
1559  f->pathlen     = pathlen;
1560  f->dev         = dev;
1561  f->next        = files->pending;
1562  files->pending = f;
1563}
1564
1565static int
1566readfile(struct bufio *f, struct header *h)
1567{
1568  /* use our own path buffer, since we use it for traversal */
1569  static struct strbuf path;
1570  struct stat          st;
1571  int                  flag;
1572  DIR                 *dir;
1573  struct dirent       *d;
1574  ssize_t              ret;
1575  dev_t                dev;
1576
1577  (void)f;
1578
1579next:
1580  flag = follow == 'L' ? 0 : AT_SYMLINK_NOFOLLOW;
1581  if (files.pending) {
1582    struct file *f;
1583
1584    f             = files.pending;
1585    files.pending = f->next;
1586    assert(f->pathlen <= path.len);
1587    path.len = f->pathlen;
1588    sbufcat(&path, f->name, f->namelen, 1024);
1589    if (follow == 'H' && f->pathlen > 0)
1590      flag &= ~AT_SYMLINK_NOFOLLOW;
1591    dev = f->dev;
1592    free(f);
1593  } else {
1594    if (!files.input)
1595      return 0;
1596    ret = getline(&path.str, &path.cap, files.input);
1597    if (ret < 0) {
1598      if (ferror(files.input))
1599        fatal("getline:");
1600      return 0;
1601    }
1602    if (ret > 0 && path.str[ret - 1] == '\n')
1603      path.str[--ret] = '\0';
1604    path.len = ret;
1605    dev      = 0;
1606  }
1607
1608  if (fstatat(AT_FDCWD, path.str, &st, flag) != 0)
1609    fatal("stat %s:", path.str);
1610  if (Xflag && dev && st.st_dev != dev)
1611    goto next;
1612  if (S_ISDIR(st.st_mode) && path.str[path.len - 1] != '/')
1613    sbufcat(&path, "/", 1, 1024);
1614  h->fields    = PATH | UID | GID | ATIME | MTIME | CTIME;
1615  h->path      = path.str;
1616  h->pathlen   = path.len;
1617  h->dev       = st.st_dev;
1618  h->ino       = st.st_ino;
1619  h->mode      = st.st_mode & ~S_IFMT;
1620  h->uid       = st.st_uid;
1621  h->gid       = st.st_gid;
1622  h->nlink     = st.st_nlink;
1623  h->rdev      = 0;
1624  h->size      = 0;
1625  h->atime     = st.st_atim;
1626  h->mtime     = st.st_mtim;
1627  h->ctime     = st.st_ctim;
1628  h->uname     = uidtouname(st.st_uid, "");
1629  h->gname     = gidtogname(st.st_gid, "");
1630  h->link      = "";
1631  h->linklen   = 0;
1632  h->slash     = NULL;
1633  h->data      = NULL;
1634  h->file      = h->path;
1635  h->fileatime = st.st_atim;
1636  h->flag      = flag;
1637  switch (st.st_mode & S_IFMT) {
1638    case S_IFREG:
1639      h->type = REGTYPE;
1640      h->size = st.st_size;
1641      break;
1642    case S_IFLNK:
1643      h->type        = SYMTYPE;
1644      h->linkbuf.len = 0;
1645      sbufalloc(&h->linkbuf, 1024, 1024);
1646      for (;;) {
1647        ret = readlink(h->path, h->linkbuf.str, h->linkbuf.cap - 1);
1648        if (ret < 0)
1649          fatal("readlink %s:", h->path);
1650        if ((size_t)ret < h->linkbuf.cap)
1651          break;
1652        if (h->linkbuf.cap > (size_t)SSIZE_MAX / 2)
1653          fatal("symlink target is too long");
1654        sbufalloc(&h->linkbuf, h->linkbuf.cap * 2, 1024);
1655      }
1656      h->linkbuf.str[ret] = '\0';
1657      h->linkbuf.len      = ret;
1658      h->link             = h->linkbuf.str;
1659      h->linklen          = h->linkbuf.len;
1660      break;
1661    case S_IFCHR:
1662      h->type = CHRTYPE;
1663      h->rdev = st.st_rdev;
1664      break;
1665    case S_IFBLK:
1666      h->type = BLKTYPE;
1667      h->rdev = st.st_rdev;
1668      break;
1669    case S_IFDIR:
1670      h->type = DIRTYPE;
1671      dir     = opendir(h->path);
1672      if (!dir)
1673        fatal("opendir %s:", h->path);
1674      for (;;) {
1675        errno = 0;
1676        d     = readdir(dir);
1677        if (!d)
1678          break;
1679        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
1680          continue;
1681        filepush(&files, d->d_name, path.len, st.st_dev);
1682      }
1683      if (errno != 0)
1684        fatal("readdir %s:", h->path);
1685      closedir(dir);
1686      break;
1687    case S_IFIFO:
1688      h->type = FIFOTYPE;
1689      break;
1690  }
1691  return 1;
1692}
1693
1694static void
1695usage(void)
1696{
1697  fprintf(stderr, "usage: pax\n");
1698  exit(2);
1699}
1700
1701static void
1702print_listopt(struct header *h)
1703{
1704  const char *s;
1705  size_t      len;
1706
1707  s = opt.listopt;
1708  while (*s) {
1709    if (*s == '%') {
1710      s++;
1711      if (*s == '%') {
1712        putchar('%');
1713        s++;
1714      } else {
1715        len = 0;
1716        while (s[len] && isalnum((unsigned char)s[len]))
1717          len++;
1718        if (len > 0) {
1719          if (strncmp(s, "path", len) == 0 && len == 4)
1720            printf("%s", h->path);
1721          else if (strncmp(s, "size", len) == 0 && len == 4)
1722            printf("%ju", (uintmax_t)h->size);
1723          else if (strncmp(s, "uid", len) == 0 && len == 3)
1724            printf("%ju", (uintmax_t)h->uid);
1725          else if (strncmp(s, "gid", len) == 0 && len == 3)
1726            printf("%ju", (uintmax_t)h->gid);
1727          else if (strncmp(s, "uname", len) == 0 && len == 5)
1728            printf("%s", h->uname);
1729          else if (strncmp(s, "gname", len) == 0 && len == 5)
1730            printf("%s", h->gname);
1731          else if (strncmp(s, "mode", len) == 0 && len == 4)
1732            printf("%04o", (unsigned int)(h->mode & 07777));
1733          else if (strncmp(s, "mtime", len) == 0 && len == 5)
1734            printf("%ju", (uintmax_t)h->mtime.tv_sec);
1735          else if (strncmp(s, "atime", len) == 0 && len == 5)
1736            printf("%ju", (uintmax_t)h->atime.tv_sec);
1737          else if (strncmp(s, "ctime", len) == 0 && len == 5)
1738            printf("%ju", (uintmax_t)h->ctime.tv_sec);
1739          else if (strncmp(s, "linkpath", len) == 0 && len == 8)
1740            printf("%s", h->link);
1741          else {
1742            putchar('%');
1743            fwrite(s, 1, len, stdout);
1744          }
1745          s += len;
1746        } else {
1747          putchar('%');
1748        }
1749      }
1750    } else if (*s == '\\') {
1751      s++;
1752      if (*s == 'n') {
1753        putchar('\n');
1754        s++;
1755      } else if (*s == 't') {
1756        putchar('\t');
1757        s++;
1758      } else if (*s == '\\') {
1759        putchar('\\');
1760        s++;
1761      } else if (*s) {
1762        putchar(*s);
1763        s++;
1764      }
1765    } else {
1766      putchar(*s);
1767      s++;
1768    }
1769  }
1770  putchar('\n');
1771}
1772
1773static void
1774parseopts(char *s)
1775{
1776  char *key, *val, *end, *d;
1777  int   ext;
1778
1779  for (;;) {
1780    s += strspn(s, " \t\n\v\f\r");
1781    if (!*s)
1782      break;
1783    key = s;
1784    while (*s && *s != ',' && *s != '=')
1785      ++s;
1786    val = NULL;
1787    end = NULL, ext = 0; /* silence gcc uninitialized warning */
1788    if (*s == '=') {
1789      ext     = s > key && s[-1] == ':';
1790      s[-ext] = '\0';
1791      val     = ++s;
1792      for (d = s; *s && *s != ','; ++s, ++d) {
1793        if (*s == '\\')
1794          ++s;
1795        if (d < s)
1796          *d = *s;
1797      }
1798      end = d;
1799    }
1800    if (*s == ',')
1801      *s++ = '\0';
1802    if (strcmp(key, "linkdata") == 0) {
1803      if (val)
1804        fatal(
1805            "option 'linkdata' must not have a "
1806            "value"
1807        );
1808      opt.linkdata = 1;
1809    } else if (strcmp(key, "times") == 0) {
1810      if (val)
1811        fatal("option 'times' must not have a value");
1812      opt.times = 1;
1813    } else if (!val) {
1814      fatal("option '%s' must have a value", key);
1815    } else if (strcmp(key, "delete") == 0) {
1816      const struct keyword *kw;
1817
1818      for (kw = keywords; kw != keywords + LEN(keywords); ++kw) {
1819        switch (fnmatch(val, kw->name, 0)) {
1820          case 0:
1821            opt.delete |= kw->field;
1822            break;
1823          case FNM_NOMATCH:
1824            break;
1825          default:
1826            fatal("fnmatch error");
1827        }
1828      }
1829    } else if (strcmp(key, "exthdr.name") == 0) {
1830      opt.exthdrname = val;
1831    } else if (strcmp(key, "globexthdr.name") == 0) {
1832      opt.globexthdrname = val;
1833    } else if (strcmp(key, "invalid") == 0) {
1834      if (strcmp(val, "bypass") != 0 && strcmp(val, "rename") != 0 && strcmp(val, "UTF-8") != 0
1835          && strcmp(val, "write") != 0) {
1836        fatal(
1837            "invalid action '%s' for option "
1838            "'invalid'",
1839            val
1840        );
1841      }
1842      opt.invalid = val;
1843    } else if (strcmp(key, "listopt") == 0) {
1844      opt.listopt = val;
1845    } else {
1846      extkeyval(ext ? &exthdr : &globexthdr, key, val, end - val);
1847    }
1848  }
1849}
1850
1851static void
1852listhdr(FILE *f, struct header *h)
1853{
1854  char        mode[11], time[13], info[23];
1855  char        unamebuf[(sizeof(uid_t) * CHAR_BIT + 2) / 3 + 1];
1856  char        gnamebuf[(sizeof(gid_t) * CHAR_BIT + 2) / 3 + 1];
1857  const char *uname, *gname, *timefmt;
1858  struct tm  *tm;
1859
1860  if (!h)
1861    return;
1862  (void)f;
1863  if (opt.listopt) {
1864    print_listopt(h);
1865    return;
1866  }
1867  if (!vflag) {
1868    printf("%s\n", h->path);
1869    return;
1870  }
1871  memset(mode, '-', sizeof mode - 1);
1872  mode[10] = '\0';
1873  switch (h->type) {
1874    case SYMTYPE:
1875      mode[0] = 'l';
1876      break;
1877    case CHRTYPE:
1878      mode[0] = 'c';
1879      break;
1880    case BLKTYPE:
1881      mode[0] = 'b';
1882      break;
1883    case DIRTYPE:
1884      mode[0] = 'd';
1885      break;
1886    case FIFOTYPE:
1887      mode[0] = 'p';
1888      break;
1889  }
1890  if (h->mode & S_IRUSR)
1891    mode[1] = 'r';
1892  if (h->mode & S_IWUSR)
1893    mode[2] = 'w';
1894  if (h->mode & S_IXUSR)
1895    mode[3] = 'x';
1896  if (h->mode & S_IRGRP)
1897    mode[4] = 'r';
1898  if (h->mode & S_IWGRP)
1899    mode[5] = 'w';
1900  if (h->mode & S_IXGRP)
1901    mode[6] = 'x';
1902  if (h->mode & S_IROTH)
1903    mode[7] = 'r';
1904  if (h->mode & S_IWOTH)
1905    mode[8] = 'w';
1906  if (h->mode & S_IXOTH)
1907    mode[9] = 'x';
1908  if (h->mode & S_ISUID)
1909    mode[3] = mode[3] == 'x' ? 's' : 'S';
1910  if (h->mode & S_ISGID)
1911    mode[3] = mode[6] == 'x' ? 's' : 'S';
1912  if (h->mode & S_ISVTX)
1913    mode[9] = mode[9] == 'x' ? 't' : 'T';
1914  uname = h->uname;
1915  if (!uname[0]) {
1916    snprintf(unamebuf, sizeof unamebuf, "%ju", (uintmax_t)h->uid);
1917    uname = unamebuf;
1918  }
1919  gname = h->gname;
1920  if (!gname[0]) {
1921    snprintf(gnamebuf, sizeof gnamebuf, "%ju", (uintmax_t)h->gid);
1922    gname = gnamebuf;
1923  }
1924  timefmt = h->mtime.tv_sec + 15780000 < curtime || h->mtime.tv_sec > curtime ? "%b %e  %Y"
1925                                                                              : "%b %e %H:%M";
1926  tm      = localtime(&h->mtime.tv_sec);
1927  if (!tm)
1928    fatal("localtime:");
1929  strftime(time, sizeof time, timefmt, tm);
1930  if (h->type == CHRTYPE || h->type == BLKTYPE)
1931    snprintf(info, sizeof info, "%u, %u", major(h->rdev), minor(h->rdev));
1932  else
1933    snprintf(info, sizeof info, "%ju", (uintmax_t)h->size);
1934  printf(
1935      "%s %2ju %-8s %-8s %9s %s %s", mode, (uintmax_t)h->nlink, uname, gname, info, time, h->path
1936  );
1937  switch (h->type) {
1938    case LNKTYPE:
1939      printf(" == %s", h->link);
1940      break;
1941    case SYMTYPE:
1942      printf(" -> %s", h->link);
1943      break;
1944  }
1945  putchar('\n');
1946}
1947
1948static void
1949mkdirp(int fd, char *name, size_t len)
1950{
1951  char *p;
1952
1953  if (len == 0)
1954    return;
1955  for (p = name + 1; p < name + len - 1; ++p) {
1956    if (*p != '/')
1957      continue;
1958    *p = 0;
1959    if (mkdirat(fd, name, 0777) != 0 && errno != EEXIST)
1960      fatal("mkdir %s:", name);
1961    *p = '/';
1962  }
1963}
1964
1965static void
1966writefile(FILE *unused, struct header *h)
1967{
1968  FILE       *f;
1969  int         fd, retry, flags;
1970  struct stat st;
1971  mode_t      mode;
1972
1973  (void)unused;
1974  if (!h)
1975    return;
1976  if (uflag && fstatat(destfd, h->path, &st, 0) == 0) {
1977    if (h->mtime.tv_sec < st.st_mtime
1978        || (h->mtime.tv_sec == st.st_mtime && h->mtime.tv_nsec < st.st_mtim.tv_nsec))
1979      return;
1980  }
1981  if (vflag)
1982    fprintf(stderr, "%s\n", h->path);
1983  if (lflag && h->file && h->type != DIRTYPE) {
1984    if (linkat(AT_FDCWD, h->file, destfd, h->path, h->flag) == 0)
1985      return;
1986  }
1987  retry = 1;
1988  if (0) {
1989  retry:
1990    retry = 0;
1991    mkdirp(destfd, h->path, h->pathlen);
1992  }
1993  mode = h->mode & ~(S_ISUID | S_ISGID);
1994  switch (h->type) {
1995    case REGTYPE:
1996      flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
1997      if (kflag)
1998        flags |= O_EXCL;
1999      fd = openat(destfd, h->path, flags, mode);
2000      if (fd < 0) {
2001        if (retry && errno == ENOENT)
2002          goto retry;
2003        fatal("open %s%s:", dest, h->path);
2004      }
2005      f = fdopen(fd, "w");
2006      if (!f)
2007        fatal("open %s:", h->path);
2008      openfile(h);
2009      copy(&bioin, h->size, f, h->size);
2010      closefile(h);
2011      fclose(f);
2012      break;
2013    case LNKTYPE:
2014      if (linkat(destfd, h->link, destfd, h->path, 0) != 0) {
2015        if (retry && errno == ENOENT)
2016          goto retry;
2017        fatal("link %s%s:", dest, h->path);
2018      }
2019      break;
2020    case SYMTYPE:
2021      if (symlinkat(h->link, destfd, h->path) != 0) {
2022        if (retry && errno == ENOENT)
2023          goto retry;
2024        fatal("symlink %s%s:", dest, h->path);
2025      }
2026      break;
2027    case CHRTYPE:
2028    case BLKTYPE:
2029      mode |= h->type == CHRTYPE ? S_IFCHR : S_IFBLK;
2030      if (mknodat(destfd, h->path, mode, h->rdev) != 0) {
2031        if (retry && errno == ENOENT)
2032          goto retry;
2033        fatal("mknod %s%s:", dest, h->path);
2034      }
2035      break;
2036    case DIRTYPE:
2037      if (mkdirat(destfd, h->path, mode) != 0) {
2038        if (retry && errno == ENOENT)
2039          goto retry;
2040        if (errno == EEXIST) {
2041          if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISDIR(st.st_mode))
2042            break;
2043          errno = EEXIST;
2044        }
2045        fatal("mkdir %s%s:", dest, h->path);
2046      }
2047      break;
2048    case FIFOTYPE:
2049      if (mkfifoat(destfd, h->path, mode) != 0) {
2050        if (retry && errno == ENOENT)
2051          goto retry;
2052        if (errno == EEXIST) {
2053          if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISFIFO(st.st_mode))
2054            break;
2055          errno = EEXIST;
2056        }
2057        fatal("mkfifo %s%s:", dest, h->path);
2058      }
2059      break;
2060  }
2061  if (preserve & (ATIME | MTIME)) {
2062    struct timespec ts[2];
2063
2064    ts[0] = preserve & ATIME ? h->atime : (struct timespec){.tv_nsec = UTIME_OMIT};
2065    ts[1] = preserve & MTIME ? h->mtime : (struct timespec){.tv_nsec = UTIME_OMIT};
2066    if (utimensat(destfd, h->path, ts, AT_SYMLINK_NOFOLLOW) != 0) {
2067      fprintf(stderr, "utimens %s%s: %s\n", dest, h->path, strerror(errno));
2068      exitstatus = 1;
2069    }
2070  }
2071  if (preserve & (UID | GID)) {
2072    uid_t uid;
2073    gid_t gid;
2074
2075    uid = preserve & UID ? unametouid(h->uname, h->uid) : (uid_t)-1;
2076    gid = preserve & GID ? gnametogid(h->gname, h->gid) : (gid_t)-1;
2077    if (fchownat(destfd, h->path, uid, gid, 0) != 0) {
2078      fprintf(stderr, "chown %s%s: %s\n", dest, h->path, strerror(errno));
2079      exitstatus = 1;
2080    } else {
2081      /* add back setuid/setgid bits if we preserved the
2082       * uid/gid */
2083      mode = h->mode;
2084    }
2085  }
2086  if (preserve & MODE && h->type != SYMTYPE) {
2087    if (fchmodat(destfd, h->path, mode, 0) != 0) {
2088      fprintf(stderr, "chmod %s%s: %s\n", dest, h->path, strerror(errno));
2089      exitstatus = 1;
2090    }
2091  }
2092}
2093
2094static int
2095match(struct header *h)
2096{
2097  static struct dir {
2098    char  *path;
2099    size_t pathlen;
2100  }            *dirs;
2101  static size_t dirslen;
2102  size_t        i;
2103
2104  if (patslen == 0)
2105    return 1;
2106  if (!dflag) {
2107    struct dir *d;
2108
2109    for (d = dirs; d < dirs + dirslen; ++d) {
2110      if (h->pathlen >= d->pathlen && memcmp(h->path, d->path, d->pathlen) == 0)
2111        return !cflag;
2112    }
2113  }
2114  for (i = 0; i < patslen; ++i) {
2115    if (nflag && patsused[i])
2116      continue;
2117    switch (fnmatch(pats[i], h->path, FNM_PATHNAME | FNM_PERIOD)) {
2118      case 0:
2119        patsused[i] = 1;
2120        if (!dflag && h->type == DIRTYPE) {
2121          struct dir *d;
2122
2123          if ((dirslen & (dirslen - 1)) == 0) {
2124            dirs = reallocarray(dirs, dirslen ? dirslen * 2 : 32, sizeof *dirs);
2125            if (!dirs)
2126              fatal(NULL);
2127          }
2128          d          = &dirs[dirslen++];
2129          d->pathlen = h->pathlen;
2130          d->path    = malloc(d->pathlen + 1);
2131          if (!d->path)
2132            fatal(NULL);
2133          memcpy(d->path, h->path, h->pathlen);
2134          /* add trailing slash if not already present */
2135          if (d->path[d->pathlen - 1] != '/')
2136            d->path[d->pathlen++] = '/';
2137        }
2138        return !cflag;
2139      case FNM_NOMATCH:
2140        break;
2141      default:
2142        fatal("fnmatch error");
2143    }
2144  }
2145  return cflag;
2146}
2147
2148static void
2149parsereplstr(char *str)
2150{
2151  static struct replstr **end = &replstr;
2152  struct replstr         *r;
2153  char                   *old, *new, delim;
2154  int                     err;
2155
2156  delim = str[0];
2157  if (!delim)
2158    usage();
2159  old = str + 1;
2160  str = strchr(old, delim);
2161  if (!str)
2162    usage();
2163  *str = 0;
2164  new  = str + 1;
2165  str  = strchr(new, delim);
2166  if (!str)
2167    usage();
2168  *str = 0;
2169
2170  r = malloc(sizeof *r);
2171  if (!r)
2172    fatal(NULL);
2173  r->next    = NULL;
2174  r->global  = 0;
2175  r->print   = 0;
2176  r->symlink = 0;
2177  for (;;) {
2178    switch (*++str) {
2179      case 'g':
2180        r->global = 1;
2181        break;
2182        // ?man -p: preserve file attributes
2183      case 'p':
2184        r->print = 1;
2185        break;
2186        // ?man -s: silent mode or print summary
2187      case 's':
2188        r->symlink = 0;
2189        break;
2190      case 'S':
2191        r->symlink = 1;
2192        break;
2193      case 0:
2194        goto done;
2195    }
2196  }
2197done:
2198  err = regcomp(&r->old, old, REG_NEWLINE);
2199  if (err != 0) {
2200    char errbuf[256];
2201
2202    regerror(err, &r->old, errbuf, sizeof errbuf);
2203    fatal("invalid regular expression: %s", errbuf);
2204  }
2205  r->new = new;
2206  *end   = r;
2207  end    = &r->next;
2208}
2209
2210static int
2211applyrepl(struct replstr *r, struct strbuf *b, const char *old, size_t oldlen)
2212{
2213  regmatch_t  match[10];
2214  size_t      i, n, l;
2215  const char *s, *p;
2216  char       *d;
2217  int         flags;
2218
2219  flags  = 0;
2220  b->len = 0;
2221  p      = old;
2222  while (regexec(&r->old, p, LEN(match), match, flags) == 0) {
2223    n = match[0].rm_so;
2224    for (s = r->new; *s; ++s) {
2225      switch (*s) {
2226        case '&':
2227          i = 0;
2228          break;
2229        case '\\':
2230          i = *++s - '0';
2231          break;
2232        default:
2233          i = -1;
2234          break;
2235      }
2236      n += i <= 9 ? match[i].rm_eo - match[i].rm_so : 1;
2237    }
2238    d = sbufalloc(b, n + 1, 1024);
2239    b->len += n;
2240    memcpy(d, p, match[0].rm_so);
2241    d += match[0].rm_so;
2242    for (s = r->new; *s; ++s) {
2243      switch (*s) {
2244        case '&':
2245          i = 0;
2246          break;
2247        case '\\':
2248          i = *++s - '0';
2249          break;
2250        default:
2251          i = -1;
2252          break;
2253      }
2254      if (i <= 9) {
2255        l = match[i].rm_eo - match[i].rm_so;
2256        memcpy(d, p + match[i].rm_so, l);
2257        d += l;
2258      } else {
2259        *d++ = *s;
2260      }
2261    }
2262    flags |= REG_NOTBOL;
2263    p += match[0].rm_eo;
2264    if (!r->global)
2265      break;
2266  }
2267  if (flags == 0)
2268    return 0;
2269  sbufcat(b, p, oldlen - (p - old), 1024);
2270  if (r->print)
2271    fprintf(stderr, "%s >> %s\n", old, b->str);
2272  return 1;
2273}
2274
2275static void
2276replace(struct header *h)
2277{
2278  static struct strbuf path, link;
2279  struct replstr      *r;
2280
2281  for (r = replstr; r; r = r->next) {
2282    if (applyrepl(r, &path, h->path, h->pathlen)) {
2283      h->path    = path.str;
2284      h->pathlen = path.len;
2285      break;
2286    }
2287  }
2288  if (h->type != LNKTYPE && h->type != SYMTYPE)
2289    return;
2290  for (r = replstr; r; r = r->next) {
2291    if (h->type == SYMTYPE && !r->symlink)
2292      continue;
2293    if (applyrepl(r, &link, h->link, h->linklen)) {
2294      h->link    = link.str;
2295      h->linklen = link.len;
2296      break;
2297    }
2298  }
2299}
2300
2301static int
2302is_invalid_name(const char *name)
2303{
2304  unsigned char c;
2305
2306  if (!name || !*name)
2307    return 1;
2308  for (; *name; name++) {
2309    c = (unsigned char)*name;
2310    if (c < 32 || c >= 127)
2311      return 1;
2312  }
2313  return 0;
2314}
2315
2316static void
2317interactiverename(struct header *h)
2318{
2319  static FILE *ttyfp = NULL;
2320  static char  ttybuf[1024];
2321  char        *res;
2322
2323  if (!iflag)
2324    return;
2325
2326  if (!ttyfp) {
2327    ttyfp = fopen("/dev/tty", "r+");
2328    if (!ttyfp)
2329      ttyfp = stdin;
2330  }
2331
2332  fprintf(stderr, "rename %s? ", h->path);
2333  fflush(stderr);
2334
2335  if (ttyfp == stdin)
2336    res = fgets(ttybuf, sizeof(ttybuf), stdin);
2337  else
2338    res = fgets(ttybuf, sizeof(ttybuf), ttyfp);
2339
2340  if (!res) {
2341    h->path    = "";
2342    h->pathlen = 0;
2343    return;
2344  }
2345
2346  ttybuf[strcspn(ttybuf, "\n")] = '\0';
2347
2348  if (ttybuf[0] == '\0')
2349    return;
2350
2351  if (strcmp(ttybuf, ".") == 0) {
2352    h->path    = "";
2353    h->pathlen = 0;
2354    return;
2355  }
2356
2357  h->pathbuf.len = 0;
2358  sbufcat(&h->pathbuf, ttybuf, strlen(ttybuf), 1024);
2359  h->path    = h->pathbuf.str;
2360  h->pathlen = h->pathbuf.len;
2361}
2362
2363static void
2364checkinvalid(struct header *h)
2365{
2366  int saved_iflag;
2367
2368  if (!opt.invalid)
2369    return;
2370  if (is_invalid_name(h->path)) {
2371    if (strcmp(opt.invalid, "bypass") == 0) {
2372      h->path    = "";
2373      h->pathlen = 0;
2374    } else if (strcmp(opt.invalid, "rename") == 0) {
2375      saved_iflag = iflag;
2376      iflag       = 1;
2377      interactiverename(h);
2378      iflag = saved_iflag;
2379    }
2380  }
2381}
2382
2383static off_t
2384locate_tar_end(const char *filename)
2385{
2386  char    buf[512];
2387  off_t   offset      = 0;
2388  int     zero_blocks = 0;
2389  int     fd, i, is_zero;
2390  ssize_t r;
2391
2392  fd = open(filename, O_RDONLY);
2393  if (fd < 0) {
2394    if (errno == ENOENT)
2395      return 0;
2396    fatal("open %s for append check:", filename);
2397  }
2398
2399  while ((r = read(fd, buf, 512)) == 512) {
2400    is_zero = 1;
2401    for (i = 0; i < 512; i++) {
2402      if (buf[i] != 0) {
2403        is_zero = 0;
2404        break;
2405      }
2406    }
2407    if (is_zero) {
2408      zero_blocks++;
2409      if (zero_blocks == 2) {
2410        close(fd);
2411        return offset;
2412      }
2413    } else {
2414      zero_blocks = 0;
2415    }
2416    offset += 512;
2417  }
2418
2419  close(fd);
2420  return offset;
2421}
2422
2423static void
2424handle_append(const char *filename, const char *algo, const char *format)
2425{
2426  off_t offset;
2427  int   fd;
2428
2429  if (!aflag)
2430    return;
2431  if (algo)
2432    fatal("cannot append to compressed archives");
2433  if (strcmp(format, "ustar") != 0 && strcmp(format, "pax") != 0)
2434    fatal("append is only supported for ustar and pax formats");
2435
2436  if (filename) {
2437    offset = locate_tar_end(filename);
2438    if (offset > 0) {
2439      fd = open(filename, O_RDWR);
2440      if (fd >= 0) {
2441        if (ftruncate(fd, offset) != 0)
2442          fatal("ftruncate %s for append:", filename);
2443        close(fd);
2444      }
2445    }
2446  }
2447}
2448
2449// ?man pax: portable archive interchange
2450// ?man read, write, and list member files of archive files
2451int
2452main(int argc, char *argv[])
2453{
2454  const char   *name = NULL, *arg, *format = "pax";
2455  const char   *algo = NULL;
2456  enum mode     mode = LIST;
2457  struct header hdr;
2458  readfn       *readhdr  = NULL;
2459  writefn      *writehdr = listhdr;
2460  FILE         *out      = NULL;
2461  pid_t         pid      = -1;
2462  int           i;
2463  size_t        l;
2464
2465  ARGBEGIN
2466  {
2467    // ?man -a: print or show all entries
2468    case 'a':
2469      aflag = 1;
2470      break;
2471    // ?man -b:str: specify block size or base directory
2472    case 'b':
2473      EARGF(usage());
2474      break;
2475    // ?man -c: print count or perform stdout action
2476    case 'c':
2477      cflag = 1;
2478      break;
2479    // ?man -d: specify directory
2480    case 'd':
2481      dflag = 1;
2482      break;
2483    // ?man -f:str: force the operation
2484    case 'f':
2485      name = EARGF(usage());
2486      break;
2487    // ?man -H: specify option flag
2488    case 'H':
2489      follow = 'H';
2490      break;
2491    // ?man -i: interactive mode or prompt for confirmation
2492    case 'i':
2493      iflag = 1;
2494      break;
2495    // ?man -j: specify option flag
2496    case 'j':
2497      algo = "bzip2";
2498      break;
2499    // ?man -J: specify option flag
2500    case 'J':
2501      algo = "xz";
2502      break;
2503    // ?man -k: specify option flag
2504    case 'k':
2505      kflag = 1;
2506      break;
2507    // ?man -l: list in long format
2508    case 'l':
2509      lflag = 1;
2510      break;
2511    // ?man -L: specify option flag
2512    case 'L':
2513      follow = 'L';
2514      break;
2515    // ?man -n: print line numbers or counts
2516    case 'n':
2517      nflag = 1;
2518      break;
2519    // ?man -o:str: specify output file
2520    case 'o':
2521      parseopts(EARGF(usage()));
2522      break;
2523    // ?man -p:str: preserve file attributes
2524    case 'p':
2525      for (arg = EARGF(usage()); *arg; ++arg) {
2526        switch (*arg) {
2527          // ?man -a: print or show all entries
2528          case 'a':
2529            preserve &= ~ATIME;
2530            break;
2531          // ?man -e: specify expression or pattern
2532          case 'e':
2533            preserve = ~0;
2534            break;
2535          // ?man -m: specify mode or limit
2536          case 'm':
2537            preserve &= ~MTIME;
2538            break;
2539          // ?man -o: specify output file
2540          case 'o':
2541            preserve |= UID | GID;
2542            break;
2543          // ?man -p: preserve file attributes
2544          case 'p':
2545            preserve |= MODE;
2546            break;
2547          default:
2548            fatal("unknown -p option");
2549        }
2550      }
2551      break;
2552    // ?man -r: operate recursively
2553    case 'r':
2554      mode |= READ;
2555      break;
2556    // ?man -s:str: silent mode or print summary
2557    case 's':
2558      parsereplstr(EARGF(usage()));
2559      break;
2560    // ?man -t: sort or specify timestamp
2561    case 't':
2562      tflag = 1;
2563      break;
2564    // ?man -u: unbuffered output
2565    case 'u':
2566      uflag = 1;
2567      break;
2568    // ?man -v: verbose mode; show progress
2569    case 'v':
2570      vflag = 1;
2571      break;
2572    // ?man -w: wait for completion
2573    case 'w':
2574      mode |= WRITE;
2575      break;
2576    // ?man -x:str: hex format or match whole lines
2577    case 'x':
2578      format = EARGF(usage());
2579      break;
2580    // ?man -X: specify option flag
2581    case 'X':
2582      Xflag = 1;
2583      break;
2584    // ?man -z: specify option flag
2585    case 'z':
2586      algo = "gzip";
2587      break;
2588    default:
2589      usage();
2590  }
2591  ARGEND;
2592
2593  curtime = time(NULL);
2594  if (curtime == (time_t)-1)
2595    fatal("time:");
2596  exthdr.fields &= ~opt.delete;
2597  exthdr.delete = exthdr.fields;
2598  globexthdr.fields &= ~opt.delete;
2599  globexthdr.delete = globexthdr.fields;
2600  if ((exthdr.fields | globexthdr.fields | opt.delete) & SIZE)
2601    fatal("field 'size' cannot be overridden or deleted");
2602
2603  switch (mode) {
2604    case READ:
2605      writehdr = writefile;
2606      /* fallthrough */
2607    case LIST:
2608      if (name && strcmp(name, "-") != 0) {
2609        bioin.fd = open(name, O_RDONLY);
2610        if (bioin.fd < 0)
2611          fatal("open %s:", name);
2612      }
2613      readhdr = detectformat(&bioin, algo, &pid);
2614      if (!readhdr)
2615        fatal("could not detect archive format");
2616      if (argc) {
2617        pats     = argv;
2618        patslen  = argc;
2619        patsused = calloc(1, argc);
2620        if (!patsused)
2621          fatal(NULL);
2622      }
2623      break;
2624    case WRITE:
2625      if (name && strcmp(name, "-") == 0)
2626        name = NULL;
2627      handle_append(name, algo, format);
2628      out = compress(algo, name, &pid);
2629      if (strcmp(format, "ustar") == 0) {
2630        writehdr = writeustar;
2631      } else if (strcmp(format, "pax") == 0) {
2632        writehdr = writepax;
2633        if (globexthdr.fields)
2634          writeexthdr(stdout, 'g', &globexthdr);
2635      } else if (strcmp(format, "cpio") == 0) {
2636        writehdr = writecpio;
2637      } else {
2638        fatal("unsupported archive format '%s'", format);
2639      }
2640      break;
2641    case COPY:
2642      if (name || argc == 0)
2643        usage();
2644      l    = strlen(argv[--argc]);
2645      dest = malloc(l + 2);
2646      if (!dest)
2647        fatal(NULL);
2648      memcpy(dest, argv[argc], l);
2649      memcpy(dest + l, "/", 2);
2650      destfd = open(dest, O_SEARCH | O_DIRECTORY);
2651      if (destfd < 0)
2652        fatal("open %s:", dest);
2653      writehdr = writefile;
2654      break;
2655  }
2656  if (mode & WRITE) {
2657    readhdr  = readfile;
2658    bioin.fd = -1;
2659    for (i = 0; i < argc; ++i)
2660      filepush(&files, argv[i], 0, 0);
2661    if (argc == 0)
2662      files.input = stdin;
2663  }
2664
2665  memset(&hdr, 0, sizeof hdr);
2666  while (readhdr(&bioin, &hdr)) {
2667    mergehdr(&hdr, &exthdr, ~0);
2668    mergehdr(&hdr, &globexthdr, ~exthdr.fields);
2669    if (match(&hdr)) {
2670      replace(&hdr);
2671      checkinvalid(&hdr);
2672      interactiverename(&hdr);
2673      if (*hdr.path)
2674        writehdr(out, &hdr);
2675    }
2676  }
2677  writehdr(out, NULL);
2678  if (out) {
2679    if (fflush(out) != 0)
2680      fatal("write:");
2681    fclose(out);
2682  }
2683  for (i = 0; i < (int)patslen; ++i) {
2684    if (!patsused[i])
2685      fatal("pattern not matched: %s", pats[i]);
2686  }
2687
2688  if (pid != -1) {
2689    int st;
2690
2691    if (waitpid(pid, &st, 0) == -1)
2692      fatal("waitpid:");
2693    if (WIFEXITED(st) && WEXITSTATUS(st) != 0)
2694      fatal("child exited with status %d", WEXITSTATUS(st));
2695    if (WIFSIGNALED(st))
2696      fatal("child terminated by signal %d", WTERMSIG(st));
2697  }
2698  return exitstatus;
2699}