toybox/toys/pending/tar.c
<<
>>
Prefs
   1/* tar.c - create/extract archives
   2 *
   3 * Copyright 2014 Ashwini Kumar <ak.ashwini81@gmail.com>
   4 *
   5 * USTAR interchange format is of interest in
   6 * See http://http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
   7 * For writing to external program
   8 * http://www.gnu.org/software/tar/manual/html_node/Writing-to-an-External-Program.html
   9
  10USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN))
  11
  12config TAR
  13  bool "tar"
  14  default n
  15  help
  16    usage: tar -[cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]
  17
  18    Create, extract, or list files from a tar file
  19
  20    Operation:
  21    c Create
  22    f Name of TARFILE ('-' for stdin/out)
  23    h Follow symlinks
  24    j (De)compress using bzip2
  25    m Don't restore mtime
  26    t List
  27    v Verbose
  28    x Extract
  29    z (De)compress using gzip
  30    C Change to DIR before operation
  31    O Extract to stdout
  32    exclude=FILE File to exclude
  33    X File with names to exclude
  34    T File with names to include
  35*/
  36
  37#define FOR_tar
  38#include "toys.h"
  39
  40GLOBALS(
  41  char *fname;
  42  char *dir;
  43  struct arg_list *inc_file;
  44  struct arg_list *exc_file;
  45  char *tocmd;
  46  struct arg_list *exc;
  47
  48  struct arg_list *inc, *pass;
  49  void *inodes, *handle;
  50)
  51
  52struct tar_hdr {
  53  char name[100], mode[8], uid[8], gid[8],size[12], mtime[12], chksum[8],
  54       type, link[100], magic[8], uname[32], gname[32], major[8], minor[8],
  55       prefix[155], padd[12];
  56};
  57
  58struct file_header {
  59  char *name, *link_target, *uname, *gname;
  60  off_t size;
  61  uid_t uid;
  62  gid_t gid;
  63  mode_t mode;
  64  time_t mtime;
  65  dev_t device;
  66};
  67
  68struct archive_handler {
  69  int src_fd;
  70  struct file_header file_hdr;
  71  off_t offset;
  72  void (*extract_handler)(struct archive_handler*);
  73};
  74
  75struct inode_list {
  76  struct inode_list *next;
  77  char *arg;
  78  ino_t ino;
  79  dev_t dev;
  80};
  81
  82static void copy_in_out(int src, int dst, off_t size)
  83{
  84  int i, rd, rem = size%512, cnt;
  85  
  86  cnt = size/512 + (rem?1:0);
  87
  88  for (i = 0; i < cnt; i++) {
  89    rd = (i == cnt-1 && rem) ? rem : 512;
  90    xreadall(src, toybuf, rd);
  91    writeall(dst, toybuf, rd);
  92  }
  93}
  94
  95//convert to octal
  96static void itoo(char *str, int len, off_t val)
  97{
  98  char *t, tmp[sizeof(off_t)*3+1];
  99  int cnt  = sprintf(tmp, "%0*llo", len, (unsigned long long)val);
 100
 101  t = tmp + cnt - len;
 102  if (*t == '0') t++;
 103  memcpy(str, t, len);
 104}
 105
 106static struct inode_list *seen_inode(void **list, struct stat *st, char *name)
 107{
 108  if (!st) llist_traverse(*list, llist_free_arg);
 109  else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
 110    struct inode_list *new;
 111
 112    for (new = *list; new; new = new->next)
 113      if(new->ino == st->st_ino && new->dev == st->st_dev)
 114        return new;
 115
 116    new = xzalloc(sizeof(*new));
 117    new->ino = st->st_ino;
 118    new->dev = st->st_dev;
 119    new->arg = xstrdup(name);
 120    new->next = *list;
 121    *list = new;
 122  }
 123  return 0;
 124}
 125
 126static void write_longname(struct archive_handler *tar, char *name, char type)
 127{
 128  struct tar_hdr tmp;
 129  unsigned int sum = 0;
 130  int i, sz = strlen(name) +1;
 131  char buf[512] = {0,};
 132
 133  memset(&tmp, 0, sizeof(tmp));
 134  strcpy(tmp.name, "././@LongLink");
 135  sprintf(tmp.mode, "%0*d", (int)sizeof(tmp.mode)-1, 0);
 136  sprintf(tmp.uid, "%0*d", (int)sizeof(tmp.uid)-1, 0);
 137  sprintf(tmp.gid, "%0*d", (int)sizeof(tmp.gid)-1, 0);
 138  sprintf(tmp.size, "%0*d", (int)sizeof(tmp.size)-1, 0);
 139  sprintf(tmp.mtime, "%0*d", (int)sizeof(tmp.mtime)-1, 0);
 140  itoo(tmp.size, sizeof(tmp.size), sz);
 141  tmp.type = type;
 142  memset(tmp.chksum, ' ', 8);
 143  strcpy(tmp.magic, "ustar  ");
 144  for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&tmp)[i];
 145  itoo(tmp.chksum, sizeof(tmp.chksum)-1, sum);
 146
 147  writeall(tar->src_fd, (void*) &tmp, sizeof(tmp));
 148  //write name to archive
 149  writeall(tar->src_fd, name, sz);
 150  if (sz%512) writeall(tar->src_fd, buf, (512-(sz%512)));
 151}
 152
 153static int filter(struct arg_list *lst, char *name)
 154{
 155  struct arg_list *cur;
 156
 157  for (cur = lst; cur; cur = cur->next)
 158    if (!fnmatch(cur->arg, name, 1<<3)) return 1;
 159  return 0;
 160}
 161
 162static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
 163{
 164  struct tar_hdr hdr;
 165  struct passwd *pw;
 166  struct group *gr;
 167  struct inode_list *node;
 168  int i, fd =-1;
 169  char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,};
 170  unsigned int sum = 0;
 171  static int warn = 1;
 172
 173  for (p = name; *p; p++)
 174    if ((p == name || p[-1] == '/') && *p != '/'
 175        && filter(TT.exc, p)) return;
 176
 177  if (S_ISDIR(st->st_mode) && name[strlen(name)-1] != '/') {
 178    lnk = xmprintf("%s/",name);
 179    free(name);
 180    *nam = name = lnk;
 181  }
 182  hname = name;
 183  //remove leading '/' or relative path '../' component
 184  if (*hname == '/') hname++;
 185  if (!*hname) return;
 186  while ((c = strstr(hname, "../"))) hname = c + 3;
 187  if (warn && hname != name) {
 188    fprintf(stderr, "removing leading '%.*s' "
 189        "from member names\n", (int)(hname-name), name);
 190    warn = 0;
 191  }
 192
 193  memset(&hdr, 0, sizeof(hdr));
 194  strncpy(hdr.name, hname, sizeof(hdr.name));
 195  itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777);
 196  itoo(hdr.uid, sizeof(hdr.uid), st->st_uid);
 197  itoo(hdr.gid, sizeof(hdr.gid), st->st_gid);
 198  itoo(hdr.size, sizeof(hdr.size), 0); //set size later
 199  itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime);
 200  for (i=0; i<sizeof(hdr.chksum); i++) hdr.chksum[i] = ' ';
 201
 202  if ((node = seen_inode(&TT.inodes, st, hname))) {
 203    //this is a hard link
 204    hdr.type = '1';
 205    if (strlen(node->arg) > sizeof(hdr.link))
 206      write_longname(tar, hname, 'K'); //write longname LINK
 207    xstrncpy(hdr.link, node->arg, sizeof(hdr.link));
 208  } else if (S_ISREG(st->st_mode)) {
 209    hdr.type = '0';
 210    if (st->st_size <= (off_t)0777777777777LL)
 211      itoo(hdr.size, sizeof(hdr.size), st->st_size);
 212    else {
 213      error_msg("can't store file '%s' of size '%lld'\n",
 214                hname, (unsigned long long)st->st_size);
 215      return;
 216    }
 217  } else if (S_ISLNK(st->st_mode)) {
 218    hdr.type = '2'; //'K' long link
 219    if (!(lnk = xreadlink(name))) {
 220      perror_msg("readlink");
 221      return;
 222    }
 223    if (strlen(lnk) > sizeof(hdr.link))
 224      write_longname(tar, hname, 'K'); //write longname LINK
 225    xstrncpy(hdr.link, lnk, sizeof(hdr.link));
 226    free(lnk);
 227  }
 228  else if (S_ISDIR(st->st_mode)) hdr.type = '5';
 229  else if (S_ISFIFO(st->st_mode)) hdr.type = '6';
 230  else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
 231    hdr.type = (S_ISCHR(st->st_mode))?'3':'4';
 232    itoo(hdr.major, sizeof(hdr.major), dev_major(st->st_rdev));
 233    itoo(hdr.minor, sizeof(hdr.minor), dev_minor(st->st_rdev));
 234  } else {
 235    error_msg("unknown file type '%o'", st->st_mode & S_IFMT);
 236    return;
 237  }
 238  if (strlen(hname) > sizeof(hdr.name))
 239          write_longname(tar, hname, 'L'); //write longname NAME
 240  strcpy(hdr.magic, "ustar  ");
 241  if ((pw = getpwuid(st->st_uid)))
 242    snprintf(hdr.uname, sizeof(hdr.uname), "%s", pw->pw_name);
 243  else snprintf(hdr.uname, sizeof(hdr.uname), "%d", st->st_uid);
 244
 245  if ((gr = getgrgid(st->st_gid)))
 246    snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name);
 247  else snprintf(hdr.gname, sizeof(hdr.gname), "%d", st->st_gid);
 248
 249  //calculate chksum.
 250  for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&hdr)[i];
 251  itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum);
 252  if (toys.optflags & FLAG_v) printf("%s\n",hname);
 253  writeall(tar->src_fd, (void*)&hdr, 512);
 254
 255  //write actual data to archive
 256  if (hdr.type != '0') return; //nothing to write
 257  if ((fd = open(name, O_RDONLY)) < 0) {
 258    perror_msg("can't open '%s'", name);
 259    return;
 260  }
 261  copy_in_out(fd, tar->src_fd, st->st_size);
 262  if (st->st_size%512) writeall(tar->src_fd, buf, (512-(st->st_size%512)));
 263  close(fd);
 264}
 265
 266static int add_to_tar(struct dirtree *node)
 267{
 268  struct stat st;
 269  char *path;
 270  struct archive_handler *hdl = (struct archive_handler*)TT.handle;
 271
 272  if (!fstat(hdl->src_fd, &st) && st.st_dev == node->st.st_dev
 273      && st.st_ino == node->st.st_ino) {
 274    error_msg("'%s' file is the archive; not dumped", TT.fname);
 275    return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0)));
 276  }
 277
 278  if (!dirtree_notdotdot(node)) return 0;
 279  path = dirtree_path(node, 0);
 280  add_file(hdl, &path, &(node->st)); //path may be modified
 281  free(path);
 282  if (toys.optflags & FLAG_no_recursion) return 0;
 283  return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0)));
 284}
 285
 286static void compress_stream(struct archive_handler *tar_hdl)
 287{
 288  int pipefd[2];
 289  pid_t cpid;
 290
 291  xpipe(pipefd);
 292
 293  signal(SIGPIPE, SIG_IGN);
 294  cpid = fork();
 295  if (cpid == -1) perror_exit("fork");
 296
 297  if (!cpid) {    /* Child reads from pipe */
 298    char *argv[] = {(toys.optflags&FLAG_z)?"gzip":"bzip2", "-f", NULL};
 299    xclose(pipefd[1]); /* Close unused write*/
 300    dup2(pipefd[0], 0);
 301    dup2(tar_hdl->src_fd, 1); //write to tar fd
 302    xexec(argv);
 303  } else {
 304    xclose(pipefd[0]);          /* Close unused read end */
 305    dup2(pipefd[1], tar_hdl->src_fd); //write to pipe
 306  }
 307}
 308
 309static void extract_to_stdout(struct archive_handler *tar)
 310{
 311  struct file_header *file_hdr = &tar->file_hdr;
 312
 313  copy_in_out(tar->src_fd, 0, file_hdr->size);
 314  tar->offset += file_hdr->size;
 315}
 316
 317static void extract_to_command(struct archive_handler *tar)
 318{
 319  int pipefd[2], status = 0;
 320  pid_t cpid;
 321  struct file_header *file_hdr = &tar->file_hdr;
 322
 323  xpipe(pipefd);
 324  if (!S_ISREG(file_hdr->mode)) return; //only regular files are supported.
 325
 326  cpid = fork();
 327  if (cpid == -1) perror_exit("fork");
 328
 329  if (!cpid) {    // Child reads from pipe
 330    char buf[64], *argv[4] = {"sh", "-c", TT.tocmd, NULL};
 331
 332    setenv("TAR_FILETYPE", "f", 1);
 333    sprintf(buf, "%0o", file_hdr->mode);
 334    setenv("TAR_MODE", buf, 1);
 335    sprintf(buf, "%ld", (long)file_hdr->size);
 336    setenv("TAR_SIZE", buf, 1);
 337    setenv("TAR_FILENAME", file_hdr->name, 1);
 338    setenv("TAR_UNAME", file_hdr->uname, 1);
 339    setenv("TAR_GNAME", file_hdr->gname, 1);
 340    sprintf(buf, "%0o", (int)file_hdr->mtime);
 341    setenv("TAR_MTIME", buf, 1);
 342    sprintf(buf, "%0o", file_hdr->uid);
 343    setenv("TAR_UID", buf, 1);
 344    sprintf(buf, "%0o", file_hdr->gid);
 345    setenv("TAR_GID", buf, 1);
 346
 347    xclose(pipefd[1]); // Close unused write
 348    dup2(pipefd[0], 0);
 349    signal(SIGPIPE, SIG_DFL);
 350    xexec(argv);
 351  } else {
 352    xclose(pipefd[0]);  // Close unused read end
 353    copy_in_out(tar->src_fd, pipefd[1], file_hdr->size);
 354    tar->offset += file_hdr->size;
 355    xclose(pipefd[1]);
 356    waitpid(cpid, &status, 0);
 357    if (WIFSIGNALED(status))
 358      xprintf("tar : %d: child returned %d\n", cpid, WTERMSIG(status));
 359  }
 360}
 361
 362static void extract_to_disk(struct archive_handler *tar)
 363{
 364  int flags, dst_fd = -1;
 365  char *s;
 366  struct stat ex;
 367  struct file_header *file_hdr = &tar->file_hdr;
 368
 369  flags = strlen(file_hdr->name);
 370  if (flags>2) {
 371    if (strstr(file_hdr->name, "/../") || !strcmp(file_hdr->name, "../") ||
 372        !strcmp(file_hdr->name+flags-3, "/.."))
 373    {
 374      error_msg("drop %s", file_hdr->name);
 375    }
 376  }
 377
 378  if (file_hdr->name[flags-1] == '/') file_hdr->name[flags-1] = 0;
 379  //Regular file with preceding path
 380  if ((s = strrchr(file_hdr->name, '/'))) {
 381    if (mkpath(file_hdr->name) && errno !=EEXIST) {
 382      error_msg(":%s: not created", file_hdr->name);
 383      return;
 384    }
 385  }
 386
 387  //remove old file, if exists
 388  if (!(toys.optflags & FLAG_k) && !S_ISDIR(file_hdr->mode)
 389      && !lstat( file_hdr->name, &ex)) {
 390    if (unlink(file_hdr->name)) {
 391      perror_msg("can't remove: %s",file_hdr->name);
 392    }
 393  }
 394
 395  //hard link
 396  if (S_ISREG(file_hdr->mode) && file_hdr->link_target) {
 397    if (link(file_hdr->link_target, file_hdr->name))
 398      perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
 399    goto COPY;
 400  }
 401
 402  switch (file_hdr->mode & S_IFMT) {
 403    case S_IFREG:
 404      flags = O_WRONLY|O_CREAT|O_EXCL;
 405      if (toys.optflags & FLAG_overwrite) flags = O_WRONLY|O_CREAT|O_TRUNC;
 406      dst_fd = open(file_hdr->name, flags, file_hdr->mode & 07777);
 407      if (dst_fd == -1) perror_msg("%s: can't open", file_hdr->name);
 408      break;
 409    case S_IFDIR:
 410      if ((mkdir(file_hdr->name, file_hdr->mode) == -1) && errno != EEXIST)
 411        perror_msg("%s: can't create", file_hdr->name);
 412      break;
 413    case S_IFLNK:
 414      if (symlink(file_hdr->link_target, file_hdr->name))
 415        perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
 416      break;
 417    case S_IFBLK:
 418    case S_IFCHR:
 419    case S_IFIFO:
 420      if (mknod(file_hdr->name, file_hdr->mode, file_hdr->device))
 421        perror_msg("can't create '%s'", file_hdr->name);
 422      break;
 423    default:
 424      printf("type not yet supported\n");
 425      break;
 426  }
 427
 428  //copy file....
 429COPY:
 430  copy_in_out(tar->src_fd, dst_fd, file_hdr->size);
 431  tar->offset += file_hdr->size;
 432  close(dst_fd);
 433
 434  if (S_ISLNK(file_hdr->mode)) return;
 435  if (!(toys.optflags & FLAG_o)) {
 436    //set ownership..., --no-same-owner, --numeric-owner
 437    uid_t u = file_hdr->uid;
 438    gid_t g = file_hdr->gid;
 439
 440    if (!(toys.optflags & FLAG_numeric_owner)) {
 441      struct group *gr = getgrnam(file_hdr->gname);
 442      struct passwd *pw = getpwnam(file_hdr->uname);
 443      if (pw) u = pw->pw_uid;
 444      if (gr) g = gr->gr_gid;
 445    }
 446    if (chown(file_hdr->name, u, g))
 447      perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);;
 448  }
 449
 450  if (toys.optflags & FLAG_p) // || !(toys.optflags & FLAG_no_same_permissions))
 451    chmod(file_hdr->name, file_hdr->mode);
 452
 453  //apply mtime
 454  if (!(toys.optflags & FLAG_m)) {
 455    struct timeval times[2] = {{file_hdr->mtime, 0},{file_hdr->mtime, 0}};
 456    utimes(file_hdr->name, times);
 457  }
 458}
 459
 460static void add_to_list(struct arg_list **llist, char *name)
 461{
 462  struct arg_list **list = llist;
 463
 464  while (*list) list=&((*list)->next);
 465  *list = xzalloc(sizeof(struct arg_list));
 466  (*list)->arg = name;
 467  if ((name[strlen(name)-1] == '/') && strlen(name) != 1)
 468    name[strlen(name)-1] = '\0';
 469}
 470
 471static void add_from_file(struct arg_list **llist, struct arg_list *flist)
 472{
 473  char *line = NULL;
 474
 475  while (flist) {
 476    int fd = 0;
 477
 478    if (strcmp((char *)flist->arg, "-"))
 479      fd = xopen((char *)flist->arg, O_RDONLY);
 480
 481    while ((line = get_line(fd))) {
 482      add_to_list(llist, line);
 483    }
 484    if (fd) close(fd);
 485    flist = flist->next;
 486  }
 487}
 488
 489static struct archive_handler *init_handler()
 490{
 491  struct archive_handler *tar_hdl = xzalloc(sizeof(struct archive_handler));
 492  tar_hdl->extract_handler = extract_to_disk;
 493  return tar_hdl;
 494}
 495
 496//convert octal to int
 497static int otoi(char *str, int len)
 498{
 499  long val;
 500  char *endp, inp[len+1]; //1 for NUL termination
 501
 502  memcpy(inp, str, len);
 503  inp[len] = '\0'; //nul-termination made sure
 504  val = strtol(inp, &endp, 8);
 505  if (*endp && *endp != ' ') error_exit("invalid param");
 506  return (int)val;
 507}
 508
 509static void extract_stream(struct archive_handler *tar_hdl)
 510{
 511  int pipefd[2];              
 512  pid_t cpid;                 
 513
 514  xpipe(pipefd);
 515
 516  cpid = fork();
 517  if (cpid == -1) perror_exit("fork");
 518
 519  if (!cpid) {    /* Child reads from pipe */
 520    char *argv[] =
 521      {(toys.optflags&FLAG_z)?"gunzip":"bunzip2", "-cf", "-", NULL};
 522    xclose(pipefd[0]); /* Close unused read*/
 523    dup2(tar_hdl->src_fd, 0);
 524    dup2(pipefd[1], 1); //write to pipe
 525    xexec(argv);
 526  } else {
 527    xclose(pipefd[1]);          /* Close unused read end */
 528    dup2(pipefd[0], tar_hdl->src_fd); //read from pipe
 529  }
 530}
 531
 532static char *process_extended_hdr(struct archive_handler *tar, int size)
 533{
 534  char *value = NULL, *p, *buf = xzalloc(size+1);
 535
 536  if (readall(tar->src_fd, buf, size) != size) error_exit("short read");
 537  buf[size] = 0;
 538  tar->offset += size;
 539  p = buf;
 540
 541  while (size) {
 542    char *key;
 543    int len, n;
 544
 545    // extended records are of the format: "LEN NAME=VALUE\n"
 546    sscanf(p, "%d %n", &len, &n);
 547    key = p + n;
 548    p += len;
 549    size -= len;
 550    p[-1] = 0;
 551    if (size < 0) {
 552      error_msg("corrupted extended header");
 553      break;
 554    }
 555
 556    len = strlen("path=");
 557    if (!strncmp(key, "path=", len)) {
 558      value = key + strlen("path=");
 559      break;
 560    }
 561  }
 562  if (value) value = xstrdup(value);
 563  free(buf);
 564  return value;
 565}
 566
 567static void tar_skip(struct archive_handler *tar, int sz)
 568{
 569  int x;
 570
 571  while ((x = lskip(tar->src_fd, sz))) {
 572    tar->offset += sz - x;
 573    sz = x;
 574  }
 575  tar->offset += sz;
 576}
 577
 578static void unpack_tar(struct archive_handler *tar_hdl)
 579{
 580  struct tar_hdr tar;
 581  struct file_header *file_hdr;
 582  int i, j, maj, min, sz, e = 0;
 583  unsigned int cksum;
 584  char *longname = NULL, *longlink = NULL;
 585
 586  while (1) {
 587    cksum = 0;
 588    if (tar_hdl->offset % 512) {
 589      sz = 512 - tar_hdl->offset % 512;
 590      tar_skip(tar_hdl, sz);
 591    }
 592    i = readall(tar_hdl->src_fd, &tar, 512);
 593    tar_hdl->offset += i;
 594    if (i != 512) {
 595      if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file
 596      error_exit("read error");
 597    }
 598
 599    if (!tar.name[0]) {
 600      if (e) return; //end of tar 2 empty blocks
 601      e = 1;//empty jump to next block
 602      continue;
 603    }
 604    if (strncmp(tar.magic, "ustar", 5)) {
 605      // Try detecting .gz or .bz2 by looking for their magic.
 606CHECK_MAGIC:
 607      if ((!strncmp(tar.name, "\x1f\x8b", 2) || !strncmp(tar.name, "BZh", 3))
 608          && !lseek(tar_hdl->src_fd, -i, SEEK_CUR)) {
 609        toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
 610        tar_hdl->offset -= i;
 611        extract_stream(tar_hdl);
 612        continue;
 613      }
 614      error_exit("invalid tar format");
 615    }
 616
 617    for (j = 0; j<148; j++) cksum += (unsigned int)((char*)&tar)[j];
 618    for (j = 156; j<500; j++) cksum += (unsigned int)((char*)&tar)[j];
 619    //cksum field itself treated as ' '
 620    for ( j= 0; j<8; j++) cksum += (unsigned int)' ';
 621
 622    if (cksum != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("wrong cksum");
 623
 624    file_hdr = &tar_hdl->file_hdr;
 625    memset(file_hdr, 0, sizeof(struct file_header));
 626    file_hdr->mode = otoi(tar.mode, sizeof(tar.mode));
 627    file_hdr->uid = otoi(tar.uid, sizeof(tar.uid));
 628    file_hdr->gid = otoi(tar.gid, sizeof(tar.gid));
 629    file_hdr->size = otoi(tar.size, sizeof(tar.size));
 630    file_hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime));
 631    file_hdr->uname = xstrdup(tar.uname);
 632    file_hdr->gname = xstrdup(tar.gname);
 633    maj = otoi(tar.major, sizeof(tar.major));
 634    min = otoi(tar.minor, sizeof(tar.minor));
 635    file_hdr->device = dev_makedev(maj, min);
 636
 637    if (tar.type <= '7') {
 638      if (tar.link[0]) {
 639        sz = sizeof(tar.link);
 640        file_hdr->link_target = xmalloc(sz + 1);
 641        memcpy(file_hdr->link_target, tar.link, sz);
 642        file_hdr->link_target[sz] = '\0';
 643      }
 644
 645      file_hdr->name = xzalloc(256);// pathname supported size
 646      if (tar.prefix[0]) {
 647        memcpy(file_hdr->name, tar.prefix, sizeof(tar.prefix));
 648        sz = strlen(file_hdr->name);
 649        if (file_hdr->name[sz-1] != '/') file_hdr->name[sz] = '/';
 650      }
 651      sz = strlen(file_hdr->name);
 652      memcpy(file_hdr->name + sz, tar.name, sizeof(tar.name));
 653      if (file_hdr->name[255]) error_exit("filename too long");
 654    }
 655
 656    switch (tar.type) {
 657      //    case '\0':
 658      case '0':
 659      case '7':
 660      case '1': //Hard Link
 661        file_hdr->mode |= S_IFREG;
 662        break;
 663      case '2':
 664        file_hdr->mode |= S_IFLNK;
 665        break;
 666      case '3':
 667        file_hdr->mode |= S_IFCHR;
 668        break;
 669      case '4':
 670        file_hdr->mode |= S_IFBLK;
 671        break;
 672      case '5':
 673        file_hdr->mode |= S_IFDIR;
 674        break;
 675      case '6':
 676        file_hdr->mode |= S_IFIFO;
 677        break;
 678      case 'K':
 679        longlink = xzalloc(file_hdr->size +1);
 680        xread(tar_hdl->src_fd, longlink, file_hdr->size);
 681        tar_hdl->offset += file_hdr->size;
 682        continue;
 683      case 'L':
 684        free(longname);
 685        longname = xzalloc(file_hdr->size +1);           
 686        xread(tar_hdl->src_fd, longname, file_hdr->size);
 687        tar_hdl->offset += file_hdr->size;
 688        continue;
 689      case 'D':
 690      case 'M':
 691      case 'N':
 692      case 'S':
 693      case 'V':
 694      case 'g':  // pax global header
 695        tar_skip(tar_hdl, file_hdr->size);
 696        continue;
 697      case 'x':  // pax extended header
 698        free(longname);
 699        longname = process_extended_hdr(tar_hdl, file_hdr->size);
 700        continue;
 701      default: break;
 702    }
 703
 704    if (longname) {
 705      free(file_hdr->name);
 706      file_hdr->name = longname;
 707      longname = NULL;
 708    }
 709    if (longlink) {
 710      free(file_hdr->link_target);
 711      file_hdr->link_target = longlink;
 712      longlink = NULL;
 713    }
 714
 715    if ((file_hdr->mode & S_IFREG) && 
 716        file_hdr->name[strlen(file_hdr->name)-1] == '/') {
 717      file_hdr->name[strlen(file_hdr->name)-1] = '\0';
 718      file_hdr->mode &= ~S_IFREG;
 719      file_hdr->mode |= S_IFDIR;
 720    }
 721
 722    if ((file_hdr->link_target && *(file_hdr->link_target)) 
 723        || S_ISLNK(file_hdr->mode) || S_ISDIR(file_hdr->mode))
 724      file_hdr->size = 0;
 725
 726    if (filter(TT.exc, file_hdr->name) ||
 727        (TT.inc && !filter(TT.inc, file_hdr->name))) goto SKIP;
 728    add_to_list(&TT.pass, xstrdup(file_hdr->name));
 729
 730    if (toys.optflags & FLAG_t) {
 731      if (toys.optflags & FLAG_v) {
 732        char perm[11];
 733        struct tm *lc = localtime((const time_t*)&(file_hdr->mtime));
 734
 735        mode_to_string(file_hdr->mode, perm);
 736        printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ",perm,file_hdr->uname,
 737            file_hdr->gname, (long)file_hdr->size, 1900+lc->tm_year,
 738            1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec);
 739      }
 740      printf("%s",file_hdr->name);
 741      if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target);
 742      xputc('\n');
 743SKIP:
 744      tar_skip(tar_hdl, file_hdr->size);
 745    } else {
 746      if (toys.optflags & FLAG_v) printf("%s\n",file_hdr->name);
 747      tar_hdl->extract_handler(tar_hdl);
 748    }
 749    free(file_hdr->name);
 750    free(file_hdr->link_target);
 751    free(file_hdr->uname);
 752    free(file_hdr->gname);
 753  }
 754}
 755
 756void tar_main(void)
 757{
 758  struct archive_handler *tar_hdl;
 759  int fd = 0;
 760  struct arg_list *tmp;
 761  char **args = toys.optargs;
 762
 763  if (!geteuid()) toys.optflags |= FLAG_p;
 764
 765  for (tmp = TT.exc; tmp; tmp = tmp->next)
 766    tmp->arg = xstrdup(tmp->arg); //freeing at the end fails otherwise
 767
 768  while(*args) add_to_list(&TT.inc, xstrdup(*args++));
 769  if (toys.optflags & FLAG_X) add_from_file(&TT.exc, TT.exc_file);
 770  if (toys.optflags & FLAG_T) add_from_file(&TT.inc, TT.inc_file);
 771
 772  if (toys.optflags & FLAG_c) {
 773    if (!TT.inc) error_exit("empty archive");
 774    fd = 1;
 775  }
 776  if ((toys.optflags & FLAG_f) && strcmp(TT.fname, "-")) 
 777    fd = xcreate(TT.fname, fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666);
 778  if (toys.optflags & FLAG_C) xchdir(TT.dir);
 779
 780  tar_hdl = init_handler();
 781  tar_hdl->src_fd = fd;
 782
 783  if ((toys.optflags & FLAG_x) || (toys.optflags & FLAG_t)) {
 784    if (toys.optflags & FLAG_O) tar_hdl->extract_handler = extract_to_stdout;
 785    if (toys.optflags & FLAG_to_command) {
 786      signal(SIGPIPE, SIG_IGN); //will be using pipe between child & parent
 787      tar_hdl->extract_handler = extract_to_command;
 788    }
 789    if (toys.optflags & FLAG_z) extract_stream(tar_hdl);
 790    unpack_tar(tar_hdl);
 791    for (tmp = TT.inc; tmp; tmp = tmp->next)
 792      if (!filter(TT.exc, tmp->arg) && !filter(TT.pass, tmp->arg))
 793        error_msg("'%s' not in archive", tmp->arg);
 794  } else if (toys.optflags & FLAG_c) {
 795    //create the tar here.
 796    if (toys.optflags & (FLAG_j|FLAG_z)) compress_stream(tar_hdl);
 797    for (tmp = TT.inc; tmp; tmp = tmp->next) {
 798      TT.handle = tar_hdl;
 799      //recurse thru dir and add files to archive
 800      dirtree_flagread(tmp->arg, DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_h),
 801        add_to_tar);
 802    }
 803    memset(toybuf, 0, 1024);
 804    writeall(tar_hdl->src_fd, toybuf, 1024);
 805    seen_inode(&TT.inodes, 0, 0);
 806  }
 807
 808  if (CFG_TOYBOX_FREE) {
 809    close(tar_hdl->src_fd);
 810    free(tar_hdl);
 811    llist_traverse(TT.exc, llist_free_arg);
 812    llist_traverse(TT.inc, llist_free_arg);
 813    llist_traverse(TT.pass, llist_free_arg);
 814  }
 815}
 816