1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini tar implementation for busybox 4 * 5 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the 6 * ground up. It still has remnents of the old code lying about, but it is 7 * very different now (i.e., cleaner, less global variables, etc.) 8 * 9 * Copyright (C) 1999,2000,2001 by Lineo, inc. 10 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> 11 * 12 * Based in part in the tar implementation in sash 13 * Copyright (c) 1999 by David I. Bell 14 * Permission is granted to use, distribute, or modify this source, 15 * provided that this copyright notice remains intact. 16 * Permission to distribute sash derived code under the GPL has been granted. 17 * 18 * Based in part on the tar implementation from busybox-0.28 19 * Copyright (C) 1995 Bruce Perens 20 * This is free software under the GNU General Public License. 21 * 22 * This program is free software; you can redistribute it and/or modify 23 * it under the terms of the GNU General Public License as published by 24 * the Free Software Foundation; either version 2 of the License, or 25 * (at your option) any later version. 26 * 27 * This program is distributed in the hope that it will be useful, 28 * but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 * General Public License for more details. 31 * 32 * You should have received a copy of the GNU General Public License 33 * along with this program; if not, write to the Free Software 34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 35 * 36 */ 37 38 39#include <stdio.h> 40#include <dirent.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <signal.h> 44#include <time.h> 45#include <utime.h> 46#include <sys/types.h> 47#include <sys/sysmacros.h> 48#include <getopt.h> 49#include <fnmatch.h> 50#include <string.h> 51#include <stdlib.h> 52#include <unistd.h> 53#include "busybox.h" 54 55/* Tar file constants */ 56#ifndef MAJOR 57#define MAJOR(dev) (((dev)>>8)&0xff) 58#define MINOR(dev) ((dev)&0xff) 59#endif 60 61enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ 62 63/* POSIX tar Header Block, from POSIX 1003.1-1990 */ 64struct TarHeader 65{ 66 /* byte offset */ 67 char name[NAME_SIZE]; /* 0-99 */ 68 char mode[8]; /* 100-107 */ 69 char uid[8]; /* 108-115 */ 70 char gid[8]; /* 116-123 */ 71 char size[12]; /* 124-135 */ 72 char mtime[12]; /* 136-147 */ 73 char chksum[8]; /* 148-155 */ 74 char typeflag; /* 156-156 */ 75 char linkname[NAME_SIZE]; /* 157-256 */ 76 char magic[6]; /* 257-262 */ 77 char version[2]; /* 263-264 */ 78 char uname[32]; /* 265-296 */ 79 char gname[32]; /* 297-328 */ 80 char devmajor[8]; /* 329-336 */ 81 char devminor[8]; /* 337-344 */ 82 char prefix[155]; /* 345-499 */ 83 char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ 84}; 85typedef struct TarHeader TarHeader; 86 87 88/* A few useful constants */ 89#define TAR_MAGIC "ustar" /* ustar and a null */ 90#define TAR_VERSION " " /* Be compatable with GNU tar format */ 91static const int TAR_MAGIC_LEN = 6; 92static const int TAR_VERSION_LEN = 2; 93static const int TAR_BLOCK_SIZE = 512; 94 95/* A nice enum with all the possible tar file content types */ 96enum TarFileType 97{ 98 REGTYPE = '0', /* regular file */ 99 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/ 100 LNKTYPE = '1', /* hard link */ 101 SYMTYPE = '2', /* symbolic link */ 102 CHRTYPE = '3', /* character special */ 103 BLKTYPE = '4', /* block special */ 104 DIRTYPE = '5', /* directory */ 105 FIFOTYPE = '6', /* FIFO special */ 106 CONTTYPE = '7', /* reserved */ 107 GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ 108 GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ 109}; 110typedef enum TarFileType TarFileType; 111 112/* This struct ignores magic, non-numeric user name, 113 * non-numeric group name, and the checksum, since 114 * these are all ignored by BusyBox tar. */ 115struct TarInfo 116{ 117 int tarFd; /* An open file descriptor for reading from the tarball */ 118 char * name; /* File name */ 119 mode_t mode; /* Unix mode, including device bits. */ 120 uid_t uid; /* Numeric UID */ 121 gid_t gid; /* Numeric GID */ 122 size_t size; /* Size of file */ 123 time_t mtime; /* Last-modified time */ 124 enum TarFileType type; /* Regular, directory, link, etc. */ 125 char * linkname; /* Name for symbolic and hard links */ 126 long devmajor; /* Major number for special device */ 127 long devminor; /* Minor number for special device */ 128}; 129typedef struct TarInfo TarInfo; 130 131/* Local procedures to restore files from a tar file. */ 132static int readTarFile(int tarFd, int extractFlag, int listFlag, 133 int tostdoutFlag, int verboseFlag, char** extractList, 134 char** excludeList); 135 136#ifdef BB_FEATURE_TAR_CREATE 137/* Local procedures to save files into a tar file. */ 138static int writeTarFile(const char* tarName, int verboseFlag, char **argv, 139 char** excludeList); 140#endif 141 142#if defined BB_FEATURE_TAR_EXCLUDE 143static struct option longopts[] = { 144 { "exclude", 1, NULL, 'e' }, 145 { NULL, 0, NULL, 0 } 146}; 147#endif 148 149extern int tar_main(int argc, char **argv) 150{ 151 char** excludeList=NULL; 152 char** extractList=NULL; 153 const char *tarName="-"; 154 const char *cwd=NULL; 155#if defined BB_FEATURE_TAR_EXCLUDE 156 int excludeListSize=0; 157 FILE *fileList; 158 char file[256]; 159#endif 160#if defined BB_FEATURE_TAR_GZIP 161 FILE *comp_file = NULL; 162 int unzipFlag = FALSE; 163#endif 164 int listFlag = FALSE; 165 int extractFlag = FALSE; 166 int createFlag = FALSE; 167 int verboseFlag = FALSE; 168 int tostdoutFlag = FALSE; 169 int status = FALSE; 170 int opt; 171 pid_t pid; 172 173 if (argc <= 1) 174 show_usage(); 175 176 if (argv[1][0] != '-') { 177 char *tmp = xmalloc(strlen(argv[1]) + 2); 178 tmp[0] = '-'; 179 strcpy(tmp + 1, argv[1]); 180 argv[1] = tmp; 181 } 182 183 while ( 184#ifndef BB_FEATURE_TAR_EXCLUDE 185 (opt = getopt(argc, argv, "cxtzvOf:pC:")) 186#else 187 (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL)) 188#endif 189 > 0) { 190 switch (opt) { 191 case 'c': 192 if (extractFlag == TRUE || listFlag == TRUE) 193 goto flagError; 194 createFlag = TRUE; 195 break; 196 case 'x': 197 if (listFlag == TRUE || createFlag == TRUE) 198 goto flagError; 199 extractFlag = TRUE; 200 break; 201 case 't': 202 if (extractFlag == TRUE || createFlag == TRUE) 203 goto flagError; 204 listFlag = TRUE; 205 break; 206#ifdef BB_FEATURE_TAR_GZIP 207 case 'z': 208 unzipFlag = TRUE; 209 break; 210#endif 211 case 'v': 212 verboseFlag = TRUE; 213 break; 214 case 'O': 215 tostdoutFlag = TRUE; 216 break; 217 case 'f': 218 if (*tarName != '-') 219 error_msg_and_die( "Only one 'f' option allowed"); 220 tarName = optarg; 221 break; 222#if defined BB_FEATURE_TAR_EXCLUDE 223 case 'e': 224 excludeList=xrealloc( excludeList, 225 sizeof(char *) * (excludeListSize+2)); 226 excludeList[excludeListSize] = optarg; 227 /* Tack a NULL onto the end of the list */ 228 excludeList[++excludeListSize] = NULL; 229 case 'X': 230 fileList = xfopen(optarg, "r"); 231 while (fgets(file, sizeof(file), fileList) != NULL) { 232 excludeList = xrealloc(excludeList, 233 sizeof(char *) * (excludeListSize+2)); 234 chomp(file); 235 excludeList[excludeListSize] = xstrdup(file); 236 /* Tack a NULL onto the end of the list */ 237 excludeList[++excludeListSize] = NULL; 238 } 239 fclose(fileList); 240 break; 241#endif 242 case 'p': 243 break; 244 case 'C': 245 cwd = xgetcwd((char *)cwd); 246 if (chdir(optarg)) { 247 printf("cd: %s: %s\n", optarg, strerror(errno)); 248 return EXIT_FAILURE; 249 } 250 break; 251 default: 252 show_usage(); 253 } 254 } 255 256 /* 257 * Do the correct type of action supplying the rest of the 258 * command line arguments as the list of files to process. 259 */ 260 if (createFlag == TRUE) { 261#ifndef BB_FEATURE_TAR_CREATE 262 error_msg_and_die( "This version of tar was not compiled with tar creation support."); 263#else 264#ifdef BB_FEATURE_TAR_GZIP 265 if (unzipFlag==TRUE) 266 error_msg_and_die("Creation of compressed not internally support by tar, pipe to busybox gunzip"); 267#endif 268 status = writeTarFile(tarName, verboseFlag, argv + optind, excludeList); 269#endif 270 } 271 if (listFlag == TRUE || extractFlag == TRUE) { 272 int tarFd; 273 if (argv[optind]) 274 extractList = argv + optind; 275 /* Open the tar file for reading. */ 276 if (!strcmp(tarName, "-")) 277 tarFd = fileno(stdin); 278 else 279 tarFd = open(tarName, O_RDONLY); 280 if (tarFd < 0) 281 perror_msg_and_die("Error opening '%s'", tarName); 282 283#ifdef BB_FEATURE_TAR_GZIP 284 /* unzip tarFd in a seperate process */ 285 if (unzipFlag == TRUE) { 286 comp_file = fdopen(tarFd, "r"); 287 288 /* set the buffer size */ 289 setvbuf(comp_file, NULL, _IOFBF, 0x8000); 290 291 if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) { 292 error_msg_and_die("Couldnt unzip file"); 293 } 294 } 295#endif 296 status = readTarFile(tarFd, extractFlag, listFlag, tostdoutFlag, 297 verboseFlag, extractList, excludeList); 298 close(tarFd); 299#ifdef BB_FEATURE_TAR_GZIP 300 if (unzipFlag == TRUE) { 301 gz_close(pid); 302 fclose(comp_file); 303 } 304#endif 305 } 306 307 if (cwd) 308 chdir(cwd); 309 if (status == TRUE) 310 return EXIT_SUCCESS; 311 else 312 return EXIT_FAILURE; 313 314 flagError: 315 error_msg_and_die( "Exactly one of 'c', 'x' or 't' must be specified"); 316} 317 318static void 319fixUpPermissions(TarInfo *header) 320{ 321 struct utimbuf t; 322 /* Now set permissions etc. for the new file */ 323 chown(header->name, header->uid, header->gid); 324 chmod(header->name, header->mode); 325 /* Reset the time */ 326 t.actime = time(0); 327 t.modtime = header->mtime; 328 utime(header->name, &t); 329} 330 331static int 332tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag) 333{ 334 size_t writeSize; 335 size_t readSize; 336 size_t actualWriteSz; 337 char buffer[20 * TAR_BLOCK_SIZE]; 338 size_t size = header->size; 339 int outFd=fileno(stdout); 340 341 /* Open the file to be written, if a file is supposed to be written */ 342 if (extractFlag==TRUE && tostdoutFlag==FALSE) { 343 /* Create the path to the file, just in case it isn't there... 344 * This should not screw up path permissions or anything. */ 345 char *dir = dirname (header->name); 346 make_directory (dir, -1, FILEUTILS_RECUR); 347 free (dir); 348 if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, 349 header->mode & ~S_IFMT)) < 0) { 350 error_msg(io_error, header->name, strerror(errno)); 351 return( FALSE); 352 } 353 } 354 355 /* Write out the file, if we are supposed to be doing that */ 356 while ( size > 0 ) { 357 actualWriteSz=0; 358 if ( size > sizeof(buffer) ) 359 writeSize = readSize = sizeof(buffer); 360 else { 361 int mod = size % TAR_BLOCK_SIZE; 362 if ( mod != 0 ) 363 readSize = size + (TAR_BLOCK_SIZE - mod); 364 else 365 readSize = size; 366 writeSize = size; 367 } 368 if ( (readSize = full_read(header->tarFd, buffer, readSize)) <= 0 ) { 369 /* Tarball seems to have a problem */ 370 error_msg("Unexpected EOF in archive"); 371 return( FALSE); 372 } 373 if ( readSize < writeSize ) 374 writeSize = readSize; 375 376 /* Write out the file, if we are supposed to be doing that */ 377 if (extractFlag==TRUE) { 378 379 if ((actualWriteSz=full_write(outFd, buffer, writeSize)) != writeSize ) { 380 /* Output file seems to have a problem */ 381 error_msg(io_error, header->name, strerror(errno)); 382 return( FALSE); 383 } 384 } else { 385 actualWriteSz=writeSize; 386 } 387 388 size -= actualWriteSz; 389 } 390 391 /* Now we are done writing the file out, so try 392 * and fix up the permissions and whatnot */ 393 if (extractFlag==TRUE && tostdoutFlag==FALSE) { 394 close(outFd); 395 fixUpPermissions(header); 396 } 397 return( TRUE); 398} 399 400static int 401tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag) 402{ 403 if (extractFlag==FALSE || tostdoutFlag==TRUE) 404 return( TRUE); 405 406 if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0) 407 return( FALSE); 408 409 fixUpPermissions(header); 410 return( TRUE); 411} 412 413static int 414tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) 415{ 416 if (extractFlag==FALSE || tostdoutFlag==TRUE) 417 return( TRUE); 418 419 if (link(header->linkname, header->name) < 0) { 420 perror_msg("%s: Cannot create hard link to '%s'", header->name, 421 header->linkname); 422 return( FALSE); 423 } 424 425 /* Now set permissions etc. for the new directory */ 426 fixUpPermissions(header); 427 return( TRUE); 428} 429 430static int 431tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) 432{ 433 if (extractFlag==FALSE || tostdoutFlag==TRUE) 434 return( TRUE); 435 436#ifdef S_ISLNK 437 if (symlink(header->linkname, header->name) < 0) { 438 perror_msg("%s: Cannot create symlink to '%s'", header->name, 439 header->linkname); 440 return( FALSE); 441 } 442 /* Try to change ownership of the symlink. 443 * If libs doesn't support that, don't bother. 444 * Changing the pointed-to-file is the Wrong Thing(tm). 445 */ 446#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) 447 lchown(header->name, header->uid, header->gid); 448#endif 449 450 /* Do not change permissions or date on symlink, 451 * since it changes the pointed to file instead. duh. */ 452#else 453 error_msg("%s: Cannot create symlink to '%s': %s", 454 header->name, header->linkname, 455 "symlinks not supported"); 456#endif 457 return( TRUE); 458} 459 460static int 461tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag) 462{ 463 if (extractFlag==FALSE || tostdoutFlag==TRUE) 464 return( TRUE); 465 466 if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) { 467 if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) { 468 perror_msg("%s: Cannot mknod", header->name); 469 return( FALSE); 470 } 471 } else if (S_ISFIFO(header->mode)) { 472 if (mkfifo(header->name, header->mode) < 0) { 473 perror_msg("%s: Cannot mkfifo", header->name); 474 return( FALSE); 475 } 476 } 477 478 /* Now set permissions etc. for the new directory */ 479 fixUpPermissions(header); 480 return( TRUE); 481} 482 483/* Parse the tar header and fill in the nice struct with the details */ 484static int 485readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) 486{ 487 int i; 488 long chksum, sum=0; 489 unsigned char *s = (unsigned char *)rawHeader; 490 491 header->name = rawHeader->name; 492 /* Check for and relativify any absolute paths */ 493 if ( *(header->name) == '/' ) { 494 static int alreadyWarned=FALSE; 495 496 while (*(header->name) == '/') 497 header->name++; 498 499 if (alreadyWarned == FALSE) { 500 error_msg("Removing leading '/' from member names"); 501 alreadyWarned = TRUE; 502 } 503 } 504 505 header->mode = strtol(rawHeader->mode, NULL, 8); 506 header->uid = strtol(rawHeader->uid, NULL, 8); 507 header->gid = strtol(rawHeader->gid, NULL, 8); 508 header->size = strtol(rawHeader->size, NULL, 8); 509 header->mtime = strtol(rawHeader->mtime, NULL, 8); 510 chksum = strtol(rawHeader->chksum, NULL, 8); 511 header->type = rawHeader->typeflag; 512 header->linkname = rawHeader->linkname; 513 header->devmajor = strtol(rawHeader->devmajor, NULL, 8); 514 header->devminor = strtol(rawHeader->devminor, NULL, 8); 515 516 /* Check the checksum */ 517 for (i = sizeof(*rawHeader); i-- != 0;) { 518 sum += *s++; 519 } 520 /* Remove the effects of the checksum field (replace 521 * with blanks for the purposes of the checksum) */ 522 s = rawHeader->chksum; 523 for (i = sizeof(rawHeader->chksum) ; i-- != 0;) { 524 sum -= *s++; 525 } 526 sum += ' ' * sizeof(rawHeader->chksum); 527 if (sum == chksum ) 528 return ( TRUE); 529 return( FALSE); 530} 531 532static int exclude_file(char **excluded_files, const char *file) 533{ 534 int i; 535 536 if (excluded_files == NULL) 537 return 0; 538 539 for (i = 0; excluded_files[i] != NULL; i++) { 540 if (excluded_files[i][0] == '/') { 541 if (fnmatch(excluded_files[i], file, 542 FNM_PATHNAME | FNM_LEADING_DIR) == 0) 543 return 1; 544 } else { 545 const char *p; 546 547 for (p = file; p[0] != '\0'; p++) { 548 if ((p == file || p[-1] == '/') && p[0] != '/' && 549 fnmatch(excluded_files[i], p, 550 FNM_PATHNAME | FNM_LEADING_DIR) == 0) 551 return 1; 552 } 553 } 554 } 555 556 return 0; 557} 558 559static int extract_file(char **extract_files, const char *file) 560{ 561 int i; 562 563 if (extract_files == NULL) 564 return 1; 565 566 for (i = 0; extract_files[i] != NULL; i++) { 567 if (fnmatch(extract_files[i], file, FNM_LEADING_DIR) == 0) 568 return 1; 569 } 570 571 return 0; 572} 573 574/* 575 * Read a tar file and extract or list the specified files within it. 576 * If the list is empty than all files are extracted or listed. 577 */ 578static int readTarFile(int tarFd, int extractFlag, int listFlag, 579 int tostdoutFlag, int verboseFlag, char** extractList, 580 char** excludeList) 581{ 582 int status; 583 int errorFlag=FALSE; 584 int skipNextHeaderFlag=FALSE; 585 TarHeader rawHeader; 586 TarInfo header; 587 588 /* Read the tar file, and iterate over it one file at a time */ 589 while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { 590 591 /* Try to read the header */ 592 if ( readTarHeader(&rawHeader, &header) == FALSE ) { 593 if ( *(header.name) == '\0' ) { 594 goto endgame; 595 } else { 596 errorFlag=TRUE; 597 error_msg("Bad tar header, skipping"); 598 continue; 599 } 600 } 601 if ( *(header.name) == '\0' ) 602 continue; 603 header.tarFd = tarFd; 604 605 /* Skip funky extra GNU headers that precede long files */ 606 if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { 607 skipNextHeaderFlag=TRUE; 608 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) 609 errorFlag = TRUE; 610 continue; 611 } 612 if ( skipNextHeaderFlag == TRUE ) { 613 skipNextHeaderFlag=FALSE; 614 error_msg(name_longer_than_foo, NAME_SIZE); 615 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) 616 errorFlag = TRUE; 617 continue; 618 } 619 620#if defined BB_FEATURE_TAR_EXCLUDE 621 if (exclude_file(excludeList, header.name)) { 622 /* There are not the droids you're looking for, move along */ 623 /* If it is a regular file, pretend to extract it with 624 * the extractFlag set to FALSE, so the junk in the tarball 625 * is properly skipped over */ 626 if ( header.type==REGTYPE || header.type==REGTYPE0 ) { 627 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) 628 errorFlag = TRUE; 629 } 630 continue; 631 } 632#endif 633 634 if (!extract_file(extractList, header.name)) { 635 /* There are not the droids you're looking for, move along */ 636 /* If it is a regular file, pretend to extract it with 637 * the extractFlag set to FALSE, so the junk in the tarball 638 * is properly skipped over */ 639 if ( header.type==REGTYPE || header.type==REGTYPE0 ) { 640 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) 641 errorFlag = TRUE; 642 } 643 continue; 644 } 645 646 if (listFlag == TRUE) { 647 /* Special treatment if the list (-t) flag is on */ 648 if (verboseFlag == TRUE) { 649 int len, len1; 650 char buf[35]; 651 struct tm *tm = localtime (&(header.mtime)); 652 653 len=printf("%s ", mode_string(header.mode)); 654 my_getpwuid(buf, header.uid); 655 if (! *buf) 656 len+=printf("%d", header.uid); 657 else 658 len+=printf("%s", buf); 659 my_getgrgid(buf, header.gid); 660 if (! *buf) 661 len+=printf("/%-d ", header.gid); 662 else 663 len+=printf("/%-s ", buf); 664 665 if (header.type==CHRTYPE || header.type==BLKTYPE) { 666 len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", 667 header.devmajor, header.devminor); 668 } else { 669 len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); 670 } 671 /* Jump through some hoops to make the columns match up */ 672 for(;(len+len1)<31;len++) 673 printf(" "); 674 printf(buf); 675 676 /* Use ISO 8610 time format */ 677 if (tm) { 678 printf ("%04d-%02d-%02d %02d:%02d:%02d ", 679 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 680 tm->tm_hour, tm->tm_min, tm->tm_sec); 681 } 682 } 683 printf("%s", header.name); 684 if (verboseFlag == TRUE) { 685 if (header.type==LNKTYPE) /* If this is a link, say so */ 686 printf(" link to %s", header.linkname); 687 else if (header.type==SYMTYPE) 688 printf(" -> %s", header.linkname); 689 } 690 printf("\n"); 691 } 692 693 /* List contents if we are supposed to do that */ 694 if (verboseFlag == TRUE && extractFlag == TRUE) { 695 /* Now the normal listing */ 696 FILE *vbFd = stdout; 697 if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr 698 vbFd = stderr; 699 fprintf(vbFd, "%s\n", header.name); 700 } 701 702 /* Remove files if we would overwrite them */ 703 if (extractFlag == TRUE && tostdoutFlag == FALSE) 704 unlink(header.name); 705 706 /* If we got here, we can be certain we have a legitimate 707 * header to work with. So work with it. */ 708 switch ( header.type ) { 709 case REGTYPE: 710 case REGTYPE0: 711 /* If the name ends in a '/' then assume it is 712 * supposed to be a directory, and fall through */ 713 if (!last_char_is(header.name,'/')) { 714 if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) 715 errorFlag=TRUE; 716 break; 717 } 718 case DIRTYPE: 719 if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) 720 errorFlag=TRUE; 721 break; 722 case LNKTYPE: 723 if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) 724 errorFlag=TRUE; 725 break; 726 case SYMTYPE: 727 if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) 728 errorFlag=TRUE; 729 break; 730 case CHRTYPE: 731 case BLKTYPE: 732 case FIFOTYPE: 733 if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) 734 errorFlag=TRUE; 735 break; 736 default: 737 error_msg("Unknown file type '%c' in tar file", header.type); 738 close( tarFd); 739 return( FALSE); 740 } 741 } 742 close(tarFd); 743 if (status > 0) { 744 /* Bummer - we read a partial header */ 745 perror_msg("Error reading tar file"); 746 return ( FALSE); 747 } 748 else if (errorFlag==TRUE) { 749 error_msg( "Error exit delayed from previous errors"); 750 return( FALSE); 751 } else 752 return( status); 753 754 /* Stuff to do when we are done */ 755endgame: 756 close( tarFd); 757 if ( *(header.name) == '\0' ) { 758 if (errorFlag==TRUE) 759 error_msg( "Error exit delayed from previous errors"); 760 else 761 return( TRUE); 762 } 763 return( FALSE); 764} 765 766 767#ifdef BB_FEATURE_TAR_CREATE 768 769/* 770** writeTarFile(), writeFileToTarball(), and writeTarHeader() are 771** the only functions that deal with the HardLinkInfo structure. 772** Even these functions use the xxxHardLinkInfo() functions. 773*/ 774typedef struct HardLinkInfo HardLinkInfo; 775struct HardLinkInfo 776{ 777 HardLinkInfo *next; /* Next entry in list */ 778 dev_t dev; /* Device number */ 779 ino_t ino; /* Inode number */ 780 short linkCount; /* (Hard) Link Count */ 781 char name[1]; /* Start of filename (must be last) */ 782}; 783 784/* Some info to be carried along when creating a new tarball */ 785struct TarBallInfo 786{ 787 char* fileName; /* File name of the tarball */ 788 int tarFd; /* Open-for-write file descriptor 789 for the tarball */ 790 struct stat statBuf; /* Stat info for the tarball, letting 791 us know the inode and device that the 792 tarball lives, so we can avoid trying 793 to include the tarball into itself */ 794 int verboseFlag; /* Whether to print extra stuff or not */ 795 char** excludeList; /* List of files to not include */ 796 HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ 797 HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ 798}; 799typedef struct TarBallInfo TarBallInfo; 800 801 802/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ 803static void 804addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, 805 short linkCount, const char *name) 806{ 807 /* Note: hlInfoHeadPtr can never be NULL! */ 808 HardLinkInfo *hlInfo; 809 810 hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); 811 if (hlInfo) { 812 hlInfo->next = *hlInfoHeadPtr; 813 *hlInfoHeadPtr = hlInfo; 814 hlInfo->dev = dev; 815 hlInfo->ino = ino; 816 hlInfo->linkCount = linkCount; 817 strcpy(hlInfo->name, name); 818 } 819 return; 820} 821 822static void 823freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr) 824{ 825 HardLinkInfo *hlInfo = NULL; 826 HardLinkInfo *hlInfoNext = NULL; 827 828 if (hlInfoHeadPtr) { 829 hlInfo = *hlInfoHeadPtr; 830 while (hlInfo) { 831 hlInfoNext = hlInfo->next; 832 free(hlInfo); 833 hlInfo = hlInfoNext; 834 } 835 *hlInfoHeadPtr = NULL; 836 } 837 return; 838} 839 840/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ 841static HardLinkInfo * 842findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) 843{ 844 while(hlInfo) { 845 if ((ino == hlInfo->ino) && (dev == hlInfo->dev)) 846 break; 847 hlInfo = hlInfo->next; 848 } 849 return(hlInfo); 850} 851 852/* Put an octal string into the specified buffer. 853 * The number is zero and space padded and possibly null padded. 854 * Returns TRUE if successful. */ 855static int putOctal (char *cp, int len, long value) 856{ 857 int tempLength; 858 char tempBuffer[32]; 859 char *tempString = tempBuffer; 860 861 /* Create a string of the specified length with an initial space, 862 * leading zeroes and the octal number, and a trailing null. */ 863 sprintf (tempString, "%0*lo", len - 1, value); 864 865 /* If the string is too large, suppress the leading space. */ 866 tempLength = strlen (tempString) + 1; 867 if (tempLength > len) { 868 tempLength--; 869 tempString++; 870 } 871 872 /* If the string is still too large, suppress the trailing null. */ 873 if (tempLength > len) 874 tempLength--; 875 876 /* If the string is still too large, fail. */ 877 if (tempLength > len) 878 return FALSE; 879 880 /* Copy the string to the field. */ 881 memcpy (cp, tempString, len); 882 883 return TRUE; 884} 885 886/* Write out a tar header for the specified file/directory/whatever */ 887static int 888writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name, 889 const char *real_name, struct stat *statbuf) 890{ 891 long chksum=0; 892 struct TarHeader header; 893 const unsigned char *cp = (const unsigned char *) &header; 894 ssize_t size = sizeof(struct TarHeader); 895 896 memset( &header, 0, size); 897 898 strncpy(header.name, header_name, sizeof(header.name)); 899 900 putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); 901 putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); 902 putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); 903 putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */ 904 putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); 905 strncpy(header.magic, TAR_MAGIC TAR_VERSION, 906 TAR_MAGIC_LEN + TAR_VERSION_LEN ); 907 908 /* Enter the user and group names (default to root if it fails) */ 909 my_getpwuid(header.uname, statbuf->st_uid); 910 if (! *header.uname) 911 strcpy(header.uname, "root"); 912 my_getgrgid(header.gname, statbuf->st_gid); 913 if (! *header.uname) 914 strcpy(header.uname, "root"); 915 916 if (tbInfo->hlInfo) { 917 /* This is a hard link */ 918 header.typeflag = LNKTYPE; 919 strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname)); 920 } else if (S_ISLNK(statbuf->st_mode)) { 921 char *lpath = xreadlink(real_name); 922 if (!lpath) /* Already printed err msg inside xreadlink() */ 923 return ( FALSE); 924 header.typeflag = SYMTYPE; 925 strncpy(header.linkname, lpath, sizeof(header.linkname)); 926 free(lpath); 927 } else if (S_ISDIR(statbuf->st_mode)) { 928 header.typeflag = DIRTYPE; 929 strncat(header.name, "/", sizeof(header.name)); 930 } else if (S_ISCHR(statbuf->st_mode)) { 931 header.typeflag = CHRTYPE; 932 putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); 933 putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); 934 } else if (S_ISBLK(statbuf->st_mode)) { 935 header.typeflag = BLKTYPE; 936 putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); 937 putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); 938 } else if (S_ISFIFO(statbuf->st_mode)) { 939 header.typeflag = FIFOTYPE; 940 } else if (S_ISREG(statbuf->st_mode)) { 941 header.typeflag = REGTYPE; 942 putOctal(header.size, sizeof(header.size), statbuf->st_size); 943 } else { 944 error_msg("%s: Unknown file type", real_name); 945 return ( FALSE); 946 } 947 948 /* Calculate and store the checksum (i.e., the sum of all of the bytes of 949 * the header). The checksum field must be filled with blanks for the 950 * calculation. The checksum field is formatted differently from the 951 * other fields: it has [6] digits, a null, then a space -- rather than 952 * digits, followed by a null like the other fields... */ 953 memset(header.chksum, ' ', sizeof(header.chksum)); 954 cp = (const unsigned char *) &header; 955 while (size-- > 0) 956 chksum += *cp++; 957 putOctal(header.chksum, 7, chksum); 958 959 /* Now write the header out to disk */ 960 if ((size=full_write(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) { 961 error_msg(io_error, real_name, strerror(errno)); 962 return ( FALSE); 963 } 964 /* Pad the header up to the tar block size */ 965 for (; size<TAR_BLOCK_SIZE; size++) { 966 write(tbInfo->tarFd, "\0", 1); 967 } 968 /* Now do the verbose thing (or not) */ 969 if (tbInfo->verboseFlag==TRUE) { 970 FILE *vbFd = stdout; 971 if (tbInfo->tarFd == fileno(stdout)) // If the archive goes to stdout, verbose to stderr 972 vbFd = stderr; 973 fprintf(vbFd, "%s\n", header.name); 974 } 975 976 return ( TRUE); 977} 978 979 980static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) 981{ 982 struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; 983 const char *header_name; 984 985 /* 986 ** Check to see if we are dealing with a hard link. 987 ** If so - 988 ** Treat the first occurance of a given dev/inode as a file while 989 ** treating any additional occurances as hard links. This is done 990 ** by adding the file information to the HardLinkInfo linked list. 991 */ 992 tbInfo->hlInfo = NULL; 993 if (statbuf->st_nlink > 1) { 994 tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, 995 statbuf->st_ino); 996 if (tbInfo->hlInfo == NULL) 997 addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, 998 statbuf->st_ino, statbuf->st_nlink, fileName); 999 } 1000 1001 /* It is against the rules to archive a socket */ 1002 if (S_ISSOCK(statbuf->st_mode)) { 1003 error_msg("%s: socket ignored", fileName); 1004 return( TRUE); 1005 } 1006 1007 /* It is a bad idea to store the archive we are in the process of creating, 1008 * so check the device and inode to be sure that this particular file isn't 1009 * the new tarball */ 1010 if (tbInfo->statBuf.st_dev == statbuf->st_dev && 1011 tbInfo->statBuf.st_ino == statbuf->st_ino) { 1012 error_msg("%s: file is the archive; skipping", fileName); 1013 return( TRUE); 1014 } 1015 1016 header_name = fileName; 1017 while (header_name[0] == '/') { 1018 static int alreadyWarned=FALSE; 1019 if (alreadyWarned==FALSE) { 1020 error_msg("Removing leading '/' from member names"); 1021 alreadyWarned=TRUE; 1022 } 1023 header_name++; 1024 } 1025 1026 if (strlen(fileName) >= NAME_SIZE) { 1027 error_msg(name_longer_than_foo, NAME_SIZE); 1028 return ( TRUE); 1029 } 1030 1031 if (header_name[0] == '\0') 1032 return TRUE; 1033 1034#if defined BB_FEATURE_TAR_EXCLUDE 1035 if (exclude_file(tbInfo->excludeList, header_name)) { 1036 return SKIP; 1037 } 1038#endif 1039 1040 if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) { 1041 return( FALSE); 1042 } 1043 1044 /* Now, if the file is a regular file, copy it out to the tarball */ 1045 if ((tbInfo->hlInfo == NULL) 1046 && (S_ISREG(statbuf->st_mode))) { 1047 int inputFileFd; 1048 char buffer[BUFSIZ]; 1049 ssize_t size=0, readSize=0; 1050 1051 /* open the file we want to archive, and make sure all is well */ 1052 if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { 1053 error_msg("%s: Cannot open: %s", fileName, strerror(errno)); 1054 return( FALSE); 1055 } 1056 1057 /* write the file to the archive */ 1058 while ( (size = full_read(inputFileFd, buffer, sizeof(buffer))) > 0 ) { 1059 if (full_write(tbInfo->tarFd, buffer, size) != size ) { 1060 /* Output file seems to have a problem */ 1061 error_msg(io_error, fileName, strerror(errno)); 1062 return( FALSE); 1063 } 1064 readSize+=size; 1065 } 1066 if (size == -1) { 1067 error_msg(io_error, fileName, strerror(errno)); 1068 return( FALSE); 1069 } 1070 /* Pad the file up to the tar block size */ 1071 for (; (readSize%TAR_BLOCK_SIZE) != 0; readSize++) { 1072 write(tbInfo->tarFd, "\0", 1); 1073 } 1074 close( inputFileFd); 1075 } 1076 1077 return( TRUE); 1078} 1079 1080static int writeTarFile(const char* tarName, int verboseFlag, char **argv, 1081 char** excludeList) 1082{ 1083 int tarFd=-1; 1084 int errorFlag=FALSE; 1085 ssize_t size; 1086 struct TarBallInfo tbInfo; 1087 tbInfo.verboseFlag = verboseFlag; 1088 tbInfo.hlInfoHead = NULL; 1089 1090 /* Make sure there is at least one file to tar up. */ 1091 if (*argv == NULL) 1092 error_msg_and_die("Cowardly refusing to create an empty archive"); 1093 1094 /* Open the tar file for writing. */ 1095 if (!strcmp(tarName, "-")) 1096 tbInfo.tarFd = fileno(stdout); 1097 else 1098 tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1099 if (tbInfo.tarFd < 0) { 1100 perror_msg( "Error opening '%s'", tarName); 1101 freeHardLinkInfo(&tbInfo.hlInfoHead); 1102 return ( FALSE); 1103 } 1104 tbInfo.excludeList=excludeList; 1105 /* Store the stat info for the tarball's file, so 1106 * can avoid including the tarball into itself.... */ 1107 if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) 1108 error_msg_and_die(io_error, tarName, strerror(errno)); 1109 1110 /* Read the directory/files and iterate over them one at a time */ 1111 while (*argv != NULL) { 1112 if (recursive_action(*argv++, TRUE, FALSE, FALSE, 1113 writeFileToTarball, writeFileToTarball, 1114 (void*) &tbInfo) == FALSE) { 1115 errorFlag = TRUE; 1116 } 1117 } 1118 /* Write two empty blocks to the end of the archive */ 1119 for (size=0; size<(2*TAR_BLOCK_SIZE); size++) { 1120 write(tbInfo.tarFd, "\0", 1); 1121 } 1122 1123 /* To be pedantically correct, we would check if the tarball 1124 * is smaller than 20 tar blocks, and pad it if it was smaller, 1125 * but that isn't necessary for GNU tar interoperability, and 1126 * so is considered a waste of space */ 1127 1128 /* Hang up the tools, close up shop, head home */ 1129 close(tarFd); 1130 if (errorFlag == TRUE) { 1131 error_msg("Error exit delayed from previous errors"); 1132 freeHardLinkInfo(&tbInfo.hlInfoHead); 1133 return(FALSE); 1134 } 1135 freeHardLinkInfo(&tbInfo.hlInfoHead); 1136 return( TRUE); 1137} 1138 1139 1140#endif 1141 1142