1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini tar implementation for busybox 4 * 5 * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg 6 * by Glenn McGrath 7 * 8 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the 9 * ground up. It still has remnants of the old code lying about, but it is 10 * very different now (i.e., cleaner, less global variables, etc.) 11 * 12 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> 13 * 14 * Based in part in the tar implementation in sash 15 * Copyright (c) 1999 by David I. Bell 16 * Permission is granted to use, distribute, or modify this source, 17 * provided that this copyright notice remains intact. 18 * Permission to distribute sash derived code under the GPL has been granted. 19 * 20 * Based in part on the tar implementation from busybox-0.28 21 * Copyright (C) 1995 Bruce Perens 22 * 23 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 24 */ 25 26#include <fnmatch.h> 27#include "libbb.h" 28#include "unarchive.h" 29/* FIXME: Stop using this non-standard feature */ 30#ifndef FNM_LEADING_DIR 31# define FNM_LEADING_DIR 0 32#endif 33 34 35//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) 36#define DBG(...) ((void)0) 37 38 39#define block_buf bb_common_bufsiz1 40 41 42#if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2 43/* Do not pass gzip flag to writeTarFile() */ 44#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \ 45 writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude) 46#endif 47 48 49#if ENABLE_FEATURE_TAR_CREATE 50 51/* 52** writeTarFile(), writeFileToTarball(), and writeTarHeader() are 53** the only functions that deal with the HardLinkInfo structure. 54** Even these functions use the xxxHardLinkInfo() functions. 55*/ 56typedef struct HardLinkInfo { 57 struct HardLinkInfo *next; /* Next entry in list */ 58 dev_t dev; /* Device number */ 59 ino_t ino; /* Inode number */ 60// short linkCount; /* (Hard) Link Count */ 61 char name[1]; /* Start of filename (must be last) */ 62} HardLinkInfo; 63 64/* Some info to be carried along when creating a new tarball */ 65typedef struct TarBallInfo { 66 int tarFd; /* Open-for-write file descriptor 67 * for the tarball */ 68 int verboseFlag; /* Whether to print extra stuff or not */ 69 const llist_t *excludeList; /* List of files to not include */ 70 HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ 71 HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ 72//TODO: save only st_dev + st_ino 73 struct stat tarFileStatBuf; /* Stat info for the tarball, letting 74 * us know the inode and device that the 75 * tarball lives, so we can avoid trying 76 * to include the tarball into itself */ 77} TarBallInfo; 78 79/* A nice enum with all the possible tar file content types */ 80enum { 81 REGTYPE = '0', /* regular file */ 82 REGTYPE0 = '\0', /* regular file (ancient bug compat) */ 83 LNKTYPE = '1', /* hard link */ 84 SYMTYPE = '2', /* symbolic link */ 85 CHRTYPE = '3', /* character special */ 86 BLKTYPE = '4', /* block special */ 87 DIRTYPE = '5', /* directory */ 88 FIFOTYPE = '6', /* FIFO special */ 89 CONTTYPE = '7', /* reserved */ 90 GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ 91 GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ 92}; 93 94/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ 95static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr, 96 struct stat *statbuf, 97 const char *fileName) 98{ 99 /* Note: hlInfoHeadPtr can never be NULL! */ 100 HardLinkInfo *hlInfo; 101 102 hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName)); 103 hlInfo->next = *hlInfoHeadPtr; 104 *hlInfoHeadPtr = hlInfo; 105 hlInfo->dev = statbuf->st_dev; 106 hlInfo->ino = statbuf->st_ino; 107// hlInfo->linkCount = statbuf->st_nlink; 108 strcpy(hlInfo->name, fileName); 109} 110 111static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr) 112{ 113 HardLinkInfo *hlInfo; 114 HardLinkInfo *hlInfoNext; 115 116 if (hlInfoHeadPtr) { 117 hlInfo = *hlInfoHeadPtr; 118 while (hlInfo) { 119 hlInfoNext = hlInfo->next; 120 free(hlInfo); 121 hlInfo = hlInfoNext; 122 } 123 *hlInfoHeadPtr = NULL; 124 } 125} 126 127/* Might be faster (and bigger) if the dev/ino were stored in numeric order ;) */ 128static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf) 129{ 130 while (hlInfo) { 131 if (statbuf->st_ino == hlInfo->ino 132 && statbuf->st_dev == hlInfo->dev 133 ) { 134 DBG("found hardlink:'%s'", hlInfo->name); 135 break; 136 } 137 hlInfo = hlInfo->next; 138 } 139 return hlInfo; 140} 141 142/* Put an octal string into the specified buffer. 143 * The number is zero padded and possibly null terminated. 144 * Stores low-order bits only if whole value does not fit. */ 145static void putOctal(char *cp, int len, off_t value) 146{ 147 char tempBuffer[sizeof(off_t)*3 + 1]; 148 char *tempString = tempBuffer; 149 int width; 150 151 width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value); 152 tempString += (width - len); 153 154 /* If string has leading zeroes, we can drop one */ 155 /* and field will have trailing '\0' */ 156 /* (increases chances of compat with other tars) */ 157 if (tempString[0] == '0') 158 tempString++; 159 160 /* Copy the string to the field */ 161 memcpy(cp, tempString, len); 162} 163#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) 164 165static void chksum_and_xwrite(int fd, struct tar_header_t* hp) 166{ 167 /* POSIX says that checksum is done on unsigned bytes 168 * (Sun and HP-UX gets it wrong... more details in 169 * GNU tar source) */ 170 const unsigned char *cp; 171 int chksum, size; 172 173 strcpy(hp->magic, "ustar "); 174 175 /* Calculate and store the checksum (i.e., the sum of all of the bytes of 176 * the header). The checksum field must be filled with blanks for the 177 * calculation. The checksum field is formatted differently from the 178 * other fields: it has 6 digits, a null, then a space -- rather than 179 * digits, followed by a null like the other fields... */ 180 memset(hp->chksum, ' ', sizeof(hp->chksum)); 181 cp = (const unsigned char *) hp; 182 chksum = 0; 183 size = sizeof(*hp); 184 do { chksum += *cp++; } while (--size); 185 putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum); 186 187 /* Now write the header out to disk */ 188 xwrite(fd, hp, sizeof(*hp)); 189} 190 191#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 192static void writeLongname(int fd, int type, const char *name, int dir) 193{ 194 static const struct { 195 char mode[8]; /* 100-107 */ 196 char uid[8]; /* 108-115 */ 197 char gid[8]; /* 116-123 */ 198 char size[12]; /* 124-135 */ 199 char mtime[12]; /* 136-147 */ 200 } prefilled = { 201 "0000000", 202 "0000000", 203 "0000000", 204 "00000000000", 205 "00000000000", 206 }; 207 struct tar_header_t header; 208 int size; 209 210 dir = !!dir; /* normalize: 0/1 */ 211 size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */ 212 /* + dir: account for possible '/' */ 213 214 memset(&header, 0, sizeof(header)); 215 strcpy(header.name, "././@LongLink"); 216 memcpy(header.mode, prefilled.mode, sizeof(prefilled)); 217 PUT_OCTAL(header.size, size); 218 header.typeflag = type; 219 chksum_and_xwrite(fd, &header); 220 221 /* Write filename[/] and pad the block. */ 222 /* dir=0: writes 'name<NUL>', pads */ 223 /* dir=1: writes 'name', writes '/<NUL>', pads */ 224 dir *= 2; 225 xwrite(fd, name, size - dir); 226 xwrite(fd, "/", dir); 227 size = (-size) & (TAR_BLOCK_SIZE-1); 228 memset(&header, 0, size); 229 xwrite(fd, &header, size); 230} 231#endif 232 233/* Write out a tar header for the specified file/directory/whatever */ 234static int writeTarHeader(struct TarBallInfo *tbInfo, 235 const char *header_name, const char *fileName, struct stat *statbuf) 236{ 237 struct tar_header_t header; 238 239 memset(&header, 0, sizeof(header)); 240 241 strncpy(header.name, header_name, sizeof(header.name)); 242 243 /* POSIX says to mask mode with 07777. */ 244 PUT_OCTAL(header.mode, statbuf->st_mode & 07777); 245 PUT_OCTAL(header.uid, statbuf->st_uid); 246 PUT_OCTAL(header.gid, statbuf->st_gid); 247 memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ 248 PUT_OCTAL(header.mtime, statbuf->st_mtime); 249 250 /* Enter the user and group names */ 251 safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); 252 safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname)); 253 254 if (tbInfo->hlInfo) { 255 /* This is a hard link */ 256 header.typeflag = LNKTYPE; 257 strncpy(header.linkname, tbInfo->hlInfo->name, 258 sizeof(header.linkname)); 259#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 260 /* Write out long linkname if needed */ 261 if (header.linkname[sizeof(header.linkname)-1]) 262 writeLongname(tbInfo->tarFd, GNULONGLINK, 263 tbInfo->hlInfo->name, 0); 264#endif 265 } else if (S_ISLNK(statbuf->st_mode)) { 266 char *lpath = xmalloc_readlink_or_warn(fileName); 267 if (!lpath) 268 return FALSE; 269 header.typeflag = SYMTYPE; 270 strncpy(header.linkname, lpath, sizeof(header.linkname)); 271#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 272 /* Write out long linkname if needed */ 273 if (header.linkname[sizeof(header.linkname)-1]) 274 writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0); 275#else 276 /* If it is larger than 100 bytes, bail out */ 277 if (header.linkname[sizeof(header.linkname)-1]) { 278 free(lpath); 279 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); 280 return FALSE; 281 } 282#endif 283 free(lpath); 284 } else if (S_ISDIR(statbuf->st_mode)) { 285 header.typeflag = DIRTYPE; 286 /* Append '/' only if there is a space for it */ 287 if (!header.name[sizeof(header.name)-1]) 288 header.name[strlen(header.name)] = '/'; 289 } else if (S_ISCHR(statbuf->st_mode)) { 290 header.typeflag = CHRTYPE; 291 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); 292 PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); 293 } else if (S_ISBLK(statbuf->st_mode)) { 294 header.typeflag = BLKTYPE; 295 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); 296 PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); 297 } else if (S_ISFIFO(statbuf->st_mode)) { 298 header.typeflag = FIFOTYPE; 299 } else if (S_ISREG(statbuf->st_mode)) { 300 if (sizeof(statbuf->st_size) > 4 301 && statbuf->st_size > (off_t)0777777777777LL 302 ) { 303 bb_error_msg_and_die("can't store file '%s' " 304 "of size %"OFF_FMT"u, aborting", 305 fileName, statbuf->st_size); 306 } 307 header.typeflag = REGTYPE; 308 PUT_OCTAL(header.size, statbuf->st_size); 309 } else { 310 bb_error_msg("%s: unknown file type", fileName); 311 return FALSE; 312 } 313 314#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 315 /* Write out long name if needed */ 316 /* (we, like GNU tar, output long linkname *before* long name) */ 317 if (header.name[sizeof(header.name)-1]) 318 writeLongname(tbInfo->tarFd, GNULONGNAME, 319 header_name, S_ISDIR(statbuf->st_mode)); 320#endif 321 322 /* Now write the header out to disk */ 323 chksum_and_xwrite(tbInfo->tarFd, &header); 324 325 /* Now do the verbose thing (or not) */ 326 if (tbInfo->verboseFlag) { 327 FILE *vbFd = stdout; 328 329 /* If archive goes to stdout, verbose goes to stderr */ 330 if (tbInfo->tarFd == STDOUT_FILENO) 331 vbFd = stderr; 332 /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ 333 /* We don't have such excesses here: for us "v" == "vv" */ 334 /* '/' is probably a GNUism */ 335 fprintf(vbFd, "%s%s\n", header_name, 336 S_ISDIR(statbuf->st_mode) ? "/" : ""); 337 } 338 339 return TRUE; 340} 341 342#if ENABLE_FEATURE_TAR_FROM 343static int exclude_file(const llist_t *excluded_files, const char *file) 344{ 345 while (excluded_files) { 346 if (excluded_files->data[0] == '/') { 347 if (fnmatch(excluded_files->data, file, 348 FNM_PATHNAME | FNM_LEADING_DIR) == 0) 349 return 1; 350 } else { 351 const char *p; 352 353 for (p = file; p[0] != '\0'; p++) { 354 if ((p == file || p[-1] == '/') 355 && p[0] != '/' 356 && fnmatch(excluded_files->data, p, 357 FNM_PATHNAME | FNM_LEADING_DIR) == 0 358 ) { 359 return 1; 360 } 361 } 362 } 363 excluded_files = excluded_files->link; 364 } 365 366 return 0; 367} 368#else 369# define exclude_file(excluded_files, file) 0 370#endif 371 372static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, 373 void *userData, int depth UNUSED_PARAM) 374{ 375 struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; 376 const char *header_name; 377 int inputFileFd = -1; 378 379 DBG("writeFileToTarball('%s')", fileName); 380 381 /* Strip leading '/' (must be before memorizing hardlink's name) */ 382 header_name = fileName; 383 while (header_name[0] == '/') { 384 static smallint warned; 385 386 if (!warned) { 387 bb_error_msg("removing leading '/' from member names"); 388 warned = 1; 389 } 390 header_name++; 391 } 392 393 if (header_name[0] == '\0') 394 return TRUE; 395 396 /* It is against the rules to archive a socket */ 397 if (S_ISSOCK(statbuf->st_mode)) { 398 bb_error_msg("%s: socket ignored", fileName); 399 return TRUE; 400 } 401 402 /* 403 * Check to see if we are dealing with a hard link. 404 * If so - 405 * Treat the first occurance of a given dev/inode as a file while 406 * treating any additional occurances as hard links. This is done 407 * by adding the file information to the HardLinkInfo linked list. 408 */ 409 tbInfo->hlInfo = NULL; 410 if (!S_ISDIR(statbuf->st_mode) && statbuf->st_nlink > 1) { 411 DBG("'%s': st_nlink > 1", header_name); 412 tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); 413 if (tbInfo->hlInfo == NULL) { 414 DBG("'%s': addHardLinkInfo", header_name); 415 addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); 416 } 417 } 418 419 /* It is a bad idea to store the archive we are in the process of creating, 420 * so check the device and inode to be sure that this particular file isn't 421 * the new tarball */ 422 if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev 423 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino 424 ) { 425 bb_error_msg("%s: file is the archive; skipping", fileName); 426 return TRUE; 427 } 428 429 if (exclude_file(tbInfo->excludeList, header_name)) 430 return SKIP; 431 432#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS 433 if (strlen(header_name) >= NAME_SIZE) { 434 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); 435 return TRUE; 436 } 437#endif 438 439 /* Is this a regular file? */ 440 if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { 441 /* open the file we want to archive, and make sure all is well */ 442 inputFileFd = open_or_warn(fileName, O_RDONLY); 443 if (inputFileFd < 0) { 444 return FALSE; 445 } 446 } 447 448 /* Add an entry to the tarball */ 449 if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { 450 return FALSE; 451 } 452 453 /* If it was a regular file, write out the body */ 454 if (inputFileFd >= 0) { 455 size_t readSize; 456 /* Write the file to the archive. */ 457 /* We record size into header first, */ 458 /* and then write out file. If file shrinks in between, */ 459 /* tar will be corrupted. So we don't allow for that. */ 460 /* NB: GNU tar 1.16 warns and pads with zeroes */ 461 /* or even seeks back and updates header */ 462 bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); 463 ////off_t readSize; 464 ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); 465 ////if (readSize != statbuf->st_size && readSize >= 0) { 466 //// bb_error_msg_and_die("short read from %s, aborting", fileName); 467 ////} 468 469 /* Check that file did not grow in between? */ 470 /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */ 471 472 close(inputFileFd); 473 474 /* Pad the file up to the tar block size */ 475 /* (a few tricks here in the name of code size) */ 476 readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); 477 memset(block_buf, 0, readSize); 478 xwrite(tbInfo->tarFd, block_buf, readSize); 479 } 480 481 return TRUE; 482} 483 484#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 485# if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2) 486# define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd) 487# endif 488/* Don't inline: vfork scares gcc and pessimizes code */ 489static void NOINLINE vfork_compressor(int tar_fd, int gzip) 490{ 491 pid_t gzipPid; 492# if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2 493 const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; 494# elif ENABLE_FEATURE_SEAMLESS_GZ 495 const char *zip_exec = "gzip"; 496# else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */ 497 const char *zip_exec = "bzip2"; 498# endif 499 // On Linux, vfork never unpauses parent early, although standard 500 // allows for that. Do we want to waste bytes checking for it? 501# define WAIT_FOR_CHILD 0 502 volatile int vfork_exec_errno = 0; 503 struct fd_pair gzipDataPipe; 504# if WAIT_FOR_CHILD 505 struct fd_pair gzipStatusPipe; 506 xpiped_pair(gzipStatusPipe); 507# endif 508 xpiped_pair(gzipDataPipe); 509 510 signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ 511 512# if defined(__GNUC__) && __GNUC__ 513 /* Avoid vfork clobbering */ 514 (void) &zip_exec; 515# endif 516 517 gzipPid = xvfork(); 518 519 if (gzipPid == 0) { 520 /* child */ 521 /* NB: close _first_, then move fds! */ 522 close(gzipDataPipe.wr); 523# if WAIT_FOR_CHILD 524 close(gzipStatusPipe.rd); 525 /* gzipStatusPipe.wr will close only on exec - 526 * parent waits for this close to happen */ 527 fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); 528# endif 529 xmove_fd(gzipDataPipe.rd, 0); 530 xmove_fd(tar_fd, 1); 531 /* exec gzip/bzip2 program/applet */ 532 BB_EXECLP(zip_exec, zip_exec, "-f", NULL); 533 vfork_exec_errno = errno; 534 _exit(EXIT_FAILURE); 535 } 536 537 /* parent */ 538 xmove_fd(gzipDataPipe.wr, tar_fd); 539 close(gzipDataPipe.rd); 540# if WAIT_FOR_CHILD 541 close(gzipStatusPipe.wr); 542 while (1) { 543 char buf; 544 int n; 545 546 /* Wait until child execs (or fails to) */ 547 n = full_read(gzipStatusPipe.rd, &buf, 1); 548 if (n < 0 /* && errno == EAGAIN */) 549 continue; /* try it again */ 550 } 551 close(gzipStatusPipe.rd); 552# endif 553 if (vfork_exec_errno) { 554 errno = vfork_exec_errno; 555 bb_perror_msg_and_die("can't execute '%s'", zip_exec); 556 } 557} 558#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */ 559 560 561/* gcc 4.2.1 inlines it, making code bigger */ 562static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, 563 int dereferenceFlag, const llist_t *include, 564 const llist_t *exclude, int gzip) 565{ 566 int errorFlag = FALSE; 567 struct TarBallInfo tbInfo; 568 569 tbInfo.hlInfoHead = NULL; 570 tbInfo.tarFd = tar_fd; 571 tbInfo.verboseFlag = verboseFlag; 572 573 /* Store the stat info for the tarball's file, so 574 * can avoid including the tarball into itself.... */ 575 if (fstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf) < 0) 576 bb_perror_msg_and_die("can't stat tar file"); 577 578#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 579 if (gzip) 580 vfork_compressor(tbInfo.tarFd, gzip); 581#endif 582 583 tbInfo.excludeList = exclude; 584 585 /* Read the directory/files and iterate over them one at a time */ 586 while (include) { 587 if (!recursive_action(include->data, ACTION_RECURSE | 588 (dereferenceFlag ? ACTION_FOLLOWLINKS : 0), 589 writeFileToTarball, writeFileToTarball, &tbInfo, 0) 590 ) { 591 errorFlag = TRUE; 592 } 593 include = include->link; 594 } 595 /* Write two empty blocks to the end of the archive */ 596 memset(block_buf, 0, 2*TAR_BLOCK_SIZE); 597 xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE); 598 599 /* To be pedantically correct, we would check if the tarball 600 * is smaller than 20 tar blocks, and pad it if it was smaller, 601 * but that isn't necessary for GNU tar interoperability, and 602 * so is considered a waste of space */ 603 604 /* Close so the child process (if any) will exit */ 605 close(tbInfo.tarFd); 606 607 /* Hang up the tools, close up shop, head home */ 608 if (ENABLE_FEATURE_CLEAN_UP) 609 freeHardLinkInfo(&tbInfo.hlInfoHead); 610 611 if (errorFlag) 612 bb_error_msg("error exit delayed from previous errors"); 613 614#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 615 if (gzip) { 616 int status; 617 if (safe_waitpid(-1, &status, 0) == -1) 618 bb_perror_msg("waitpid"); 619 else if (!WIFEXITED(status) || WEXITSTATUS(status)) 620 /* gzip was killed or has exited with nonzero! */ 621 errorFlag = TRUE; 622 } 623#endif 624 return errorFlag; 625} 626#else 627int writeTarFile(int tar_fd, int verboseFlag, 628 int dereferenceFlag, const llist_t *include, 629 const llist_t *exclude, int gzip); 630#endif /* FEATURE_TAR_CREATE */ 631 632#if ENABLE_FEATURE_TAR_FROM 633static llist_t *append_file_list_to_list(llist_t *list) 634{ 635 FILE *src_stream; 636 char *line; 637 llist_t *newlist = NULL; 638 639 while (list) { 640 src_stream = xfopen_for_read(llist_pop(&list)); 641 while ((line = xmalloc_fgetline(src_stream)) != NULL) { 642 /* kill trailing '/' unless the string is just "/" */ 643 char *cp = last_char_is(line, '/'); 644 if (cp > line) 645 *cp = '\0'; 646 llist_add_to(&newlist, line); 647 } 648 fclose(src_stream); 649 } 650 return newlist; 651} 652#else 653# define append_file_list_to_list(x) 0 654#endif 655 656#if ENABLE_FEATURE_SEAMLESS_Z 657static char FAST_FUNC get_header_tar_Z(archive_handle_t *archive_handle) 658{ 659 /* Can't lseek over pipes */ 660 archive_handle->seek = seek_by_read; 661 662 /* do the decompression, and cleanup */ 663 if (xread_char(archive_handle->src_fd) != 0x1f 664 || xread_char(archive_handle->src_fd) != 0x9d 665 ) { 666 bb_error_msg_and_die("invalid magic"); 667 } 668 669 open_transformer(archive_handle->src_fd, unpack_Z_stream, "uncompress"); 670 archive_handle->offset = 0; 671 while (get_header_tar(archive_handle) == EXIT_SUCCESS) 672 continue; 673 674 /* Can only do one file at a time */ 675 return EXIT_FAILURE; 676} 677#else 678# define get_header_tar_Z NULL 679#endif 680 681#ifdef CHECK_FOR_CHILD_EXITCODE 682/* Looks like it isn't needed - tar detects malformed (truncated) 683 * archive if e.g. bunzip2 fails */ 684static int child_error; 685 686static void handle_SIGCHLD(int status) 687{ 688 /* Actually, 'status' is a signo. We reuse it for other needs */ 689 690 /* Wait for any child without blocking */ 691 if (wait_any_nohang(&status) < 0) 692 /* wait failed?! I'm confused... */ 693 return; 694 695 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 696 /* child exited with 0 */ 697 return; 698 /* Cannot happen? 699 if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */ 700 child_error = 1; 701} 702#endif 703 704//usage:#define tar_trivial_usage 705//usage: "-[" IF_FEATURE_TAR_CREATE("c") "xt" IF_FEATURE_SEAMLESS_GZ("z") 706//usage: IF_FEATURE_SEAMLESS_BZ2("j") IF_FEATURE_SEAMLESS_LZMA("a") 707//usage: IF_FEATURE_SEAMLESS_Z("Z") IF_FEATURE_TAR_NOPRESERVE_TIME("m") "vO] " 708//usage: IF_FEATURE_TAR_FROM("[-X FILE] ") 709//usage: "[-f TARFILE] [-C DIR] [FILE]..." 710//usage:#define tar_full_usage "\n\n" 711//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") 712//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") 713//usage: "or list files from a tar file\n" 714//usage: "\nOperation:" 715//usage: IF_FEATURE_TAR_CREATE( 716//usage: "\n c Create" 717//usage: ) 718//usage: "\n x Extract" 719//usage: "\n t List" 720//usage: "\nOptions:" 721//usage: "\n f Name of TARFILE ('-' for stdin/out)" 722//usage: "\n C Change to DIR before operation" 723//usage: "\n v Verbose" 724//usage: IF_FEATURE_SEAMLESS_GZ( 725//usage: "\n z (De)compress using gzip" 726//usage: ) 727//usage: IF_FEATURE_SEAMLESS_BZ2( 728//usage: "\n j (De)compress using bzip2" 729//usage: ) 730//usage: IF_FEATURE_SEAMLESS_LZMA( 731//usage: "\n a (De)compress using lzma" 732//usage: ) 733//usage: IF_FEATURE_SEAMLESS_Z( 734//usage: "\n Z (De)compress using compress" 735//usage: ) 736//usage: "\n O Extract to stdout" 737//usage: IF_FEATURE_TAR_CREATE( 738//usage: "\n h Follow symlinks" 739//usage: ) 740//usage: IF_FEATURE_TAR_NOPRESERVE_TIME( 741//usage: "\n m Don't restore mtime" 742//usage: ) 743//usage: IF_FEATURE_TAR_FROM( 744//usage: IF_FEATURE_TAR_LONG_OPTIONS( 745//usage: "\n exclude File to exclude" 746//usage: ) 747//usage: "\n X File with names to exclude" 748//usage: "\n T File with names to include" 749//usage: ) 750//usage: 751//usage:#define tar_example_usage 752//usage: "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" 753//usage: "$ tar -cf /tmp/tarball.tar /usr/local\n" 754 755// Supported but aren't in --help: 756// o no-same-owner 757// p same-permissions 758// k keep-old 759// numeric-owner 760// no-same-permissions 761// overwrite 762//IF_FEATURE_TAR_TO_COMMAND( 763// to-command 764//) 765 766enum { 767 OPTBIT_KEEP_OLD = 8, 768 IF_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) 769 IF_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,) 770 IF_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2 ,) 771 IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,) 772 IF_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,) 773 IF_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) 774 IF_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) 775 IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) // 16th bit 776 IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) 777#if ENABLE_FEATURE_TAR_LONG_OPTIONS 778 IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) 779 OPTBIT_NUMERIC_OWNER, 780 OPTBIT_NOPRESERVE_PERM, 781 OPTBIT_OVERWRITE, 782#endif 783 OPT_TEST = 1 << 0, // t 784 OPT_EXTRACT = 1 << 1, // x 785 OPT_BASEDIR = 1 << 2, // C 786 OPT_TARNAME = 1 << 3, // f 787 OPT_2STDOUT = 1 << 4, // O 788 OPT_NOPRESERVE_OWNER = 1 << 5, // o == no-same-owner 789 OPT_P = 1 << 6, // p 790 OPT_VERBOSE = 1 << 7, // v 791 OPT_KEEP_OLD = 1 << 8, // k 792 OPT_CREATE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_CREATE )) + 0, // c 793 OPT_DEREFERENCE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_DEREFERENCE )) + 0, // h 794 OPT_BZIP2 = IF_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2 )) + 0, // j 795 OPT_LZMA = IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA )) + 0, // a 796 OPT_INCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_INCLUDE_FROM)) + 0, // T 797 OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X 798 OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z 799 OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z 800 OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m 801 OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command 802 OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner 803 OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions 804 OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite 805}; 806#if ENABLE_FEATURE_TAR_LONG_OPTIONS 807static const char tar_longopts[] ALIGN1 = 808 "list\0" No_argument "t" 809 "extract\0" No_argument "x" 810 "directory\0" Required_argument "C" 811 "file\0" Required_argument "f" 812 "to-stdout\0" No_argument "O" 813 /* do not restore owner */ 814 /* Note: GNU tar handles 'o' as no-same-owner only on extract, 815 * on create, 'o' is --old-archive. We do not support --old-archive. */ 816 "no-same-owner\0" No_argument "o" 817 "same-permissions\0" No_argument "p" 818 "verbose\0" No_argument "v" 819 "keep-old\0" No_argument "k" 820# if ENABLE_FEATURE_TAR_CREATE 821 "create\0" No_argument "c" 822 "dereference\0" No_argument "h" 823# endif 824# if ENABLE_FEATURE_SEAMLESS_BZ2 825 "bzip2\0" No_argument "j" 826# endif 827# if ENABLE_FEATURE_SEAMLESS_LZMA 828 "lzma\0" No_argument "a" 829# endif 830# if ENABLE_FEATURE_TAR_FROM 831 "files-from\0" Required_argument "T" 832 "exclude-from\0" Required_argument "X" 833# endif 834# if ENABLE_FEATURE_SEAMLESS_GZ 835 "gzip\0" No_argument "z" 836# endif 837# if ENABLE_FEATURE_SEAMLESS_Z 838 "compress\0" No_argument "Z" 839# endif 840# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME 841 "touch\0" No_argument "m" 842# endif 843# if ENABLE_FEATURE_TAR_TO_COMMAND 844 "to-command\0" Required_argument "\xfb" 845# endif 846 /* use numeric uid/gid from tar header, not textual */ 847 "numeric-owner\0" No_argument "\xfc" 848 /* do not restore mode */ 849 "no-same-permissions\0" No_argument "\xfd" 850 /* on unpack, open with O_TRUNC and !O_EXCL */ 851 "overwrite\0" No_argument "\xfe" 852 /* --exclude takes next bit position in option mask, */ 853 /* therefore we have to put it _after_ --no-same-permissions */ 854# if ENABLE_FEATURE_TAR_FROM 855 "exclude\0" Required_argument "\xff" 856# endif 857 ; 858#endif 859 860int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 861int tar_main(int argc UNUSED_PARAM, char **argv) 862{ 863 char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar; 864 archive_handle_t *tar_handle; 865 char *base_dir = NULL; 866 const char *tar_filename = "-"; 867 unsigned opt; 868 int verboseFlag = 0; 869#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM 870 llist_t *excludes = NULL; 871#endif 872 873 /* Initialise default values */ 874 tar_handle = init_handle(); 875 tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS 876 | ARCHIVE_RESTORE_DATE 877 | ARCHIVE_UNLINK_OLD; 878 879 /* Apparently only root's tar preserves perms (see bug 3844) */ 880 if (getuid() != 0) 881 tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; 882 883 /* Prepend '-' to the first argument if required */ 884 opt_complementary = "--:" // first arg is options 885 "tt:vv:" // count -t,-v 886 "?:" // bail out with usage instead of error return 887 "X::T::" // cumulative lists 888#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM 889 "\xff::" // cumulative lists for --exclude 890#endif 891 IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd 892 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive 893 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive 894#if ENABLE_FEATURE_TAR_LONG_OPTIONS 895 applet_long_options = tar_longopts; 896#endif 897#if ENABLE_DESKTOP 898 if (argv[1] && argv[1][0] != '-') { 899 /* Compat: 900 * 1st argument without dash handles options with parameters 901 * differently from dashed one: it takes *next argv[i]* 902 * as paramenter even if there are more chars in 1st argument: 903 * "tar fx TARFILE" - "x" is not taken as f's param 904 * but is interpreted as -x option 905 * "tar -xf TARFILE" - dashed equivalent of the above 906 * "tar -fx ..." - "x" is taken as f's param 907 * getopt32 wouldn't handle 1st command correctly. 908 * Unfortunately, people do use such commands. 909 * We massage argv[1] to work around it by moving 'f' 910 * to the end of the string. 911 * More contrived "tar fCx TARFILE DIR" still fails, 912 * but such commands are much less likely to be used. 913 */ 914 char *f = strchr(argv[1], 'f'); 915 if (f) { 916 while (f[1] != '\0') { 917 *f = f[1]; 918 f++; 919 } 920 *f = 'f'; 921 } 922 } 923#endif 924 opt = getopt32(argv, 925 "txC:f:Oopvk" 926 IF_FEATURE_TAR_CREATE( "ch" ) 927 IF_FEATURE_SEAMLESS_BZ2( "j" ) 928 IF_FEATURE_SEAMLESS_LZMA("a" ) 929 IF_FEATURE_TAR_FROM( "T:X:") 930 IF_FEATURE_SEAMLESS_GZ( "z" ) 931 IF_FEATURE_SEAMLESS_Z( "Z" ) 932 IF_FEATURE_TAR_NOPRESERVE_TIME("m") 933 , &base_dir // -C dir 934 , &tar_filename // -f filename 935 IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T 936 IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X 937 IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command 938#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM 939 , &excludes // --exclude 940#endif 941 , &verboseFlag // combined count for -t and -v 942 , &verboseFlag // combined count for -t and -v 943 ); 944 //bb_error_msg("opt:%08x", opt); 945 argv += optind; 946 947 if (verboseFlag) tar_handle->action_header = header_verbose_list; 948 if (verboseFlag == 1) tar_handle->action_header = header_list; 949 950 if (opt & OPT_EXTRACT) 951 tar_handle->action_data = data_extract_all; 952 953 if (opt & OPT_2STDOUT) 954 tar_handle->action_data = data_extract_to_stdout; 955 956 if (opt & OPT_2COMMAND) { 957 putenv((char*)"TAR_FILETYPE=f"); 958 signal(SIGPIPE, SIG_IGN); 959 tar_handle->action_data = data_extract_to_command; 960 } 961 962 if (opt & OPT_KEEP_OLD) 963 tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; 964 965 if (opt & OPT_NUMERIC_OWNER) 966 tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER; 967 968 if (opt & OPT_NOPRESERVE_OWNER) 969 tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_OWNER; 970 971 if (opt & OPT_NOPRESERVE_PERM) 972 tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; 973 974 if (opt & OPT_OVERWRITE) { 975 tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; 976 tar_handle->ah_flags |= ARCHIVE_O_TRUNC; 977 } 978 979 if (opt & OPT_GZIP) 980 get_header_ptr = get_header_tar_gz; 981 982 if (opt & OPT_BZIP2) 983 get_header_ptr = get_header_tar_bz2; 984 985 if (opt & OPT_LZMA) 986 get_header_ptr = get_header_tar_lzma; 987 988 if (opt & OPT_COMPRESS) 989 get_header_ptr = get_header_tar_Z; 990 991 if (opt & OPT_NOPRESERVE_TIME) 992 tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE; 993 994#if ENABLE_FEATURE_TAR_FROM 995 tar_handle->reject = append_file_list_to_list(tar_handle->reject); 996# if ENABLE_FEATURE_TAR_LONG_OPTIONS 997 /* Append excludes to reject */ 998 while (excludes) { 999 llist_t *next = excludes->link; 1000 excludes->link = tar_handle->reject; 1001 tar_handle->reject = excludes; 1002 excludes = next; 1003 } 1004# endif 1005 tar_handle->accept = append_file_list_to_list(tar_handle->accept); 1006#endif 1007 1008 /* Setup an array of filenames to work with */ 1009 /* TODO: This is the same as in ar, make a separate function? */ 1010 while (*argv) { 1011 /* kill trailing '/' unless the string is just "/" */ 1012 char *cp = last_char_is(*argv, '/'); 1013 if (cp > *argv) 1014 *cp = '\0'; 1015 llist_add_to_end(&tar_handle->accept, *argv); 1016 argv++; 1017 } 1018 1019 if (tar_handle->accept || tar_handle->reject) 1020 tar_handle->filter = filter_accept_reject_list; 1021 1022 /* Open the tar file */ 1023 { 1024 int tar_fd = STDIN_FILENO; 1025 int flags = O_RDONLY; 1026 1027 if (opt & OPT_CREATE) { 1028 /* Make sure there is at least one file to tar up */ 1029 if (tar_handle->accept == NULL) 1030 bb_error_msg_and_die("empty archive"); 1031 1032 tar_fd = STDOUT_FILENO; 1033 /* Mimicking GNU tar 1.15.1: */ 1034 flags = O_WRONLY | O_CREAT | O_TRUNC; 1035 } 1036 1037 if (LONE_DASH(tar_filename)) { 1038 tar_handle->src_fd = tar_fd; 1039 tar_handle->seek = seek_by_read; 1040 } else { 1041 if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) { 1042 get_header_ptr = get_header_tar; 1043 tar_handle->src_fd = open_zipped(tar_filename); 1044 if (tar_handle->src_fd < 0) 1045 bb_perror_msg_and_die("can't open '%s'", tar_filename); 1046 } else { 1047 tar_handle->src_fd = xopen(tar_filename, flags); 1048 } 1049 } 1050 } 1051 1052 if (base_dir) 1053 xchdir(base_dir); 1054 1055#ifdef CHECK_FOR_CHILD_EXITCODE 1056 /* We need to know whether child (gzip/bzip/etc) exits abnormally */ 1057 signal(SIGCHLD, handle_SIGCHLD); 1058#endif 1059 1060 /* Create an archive */ 1061 if (opt & OPT_CREATE) { 1062#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 1063 int zipMode = 0; 1064 if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP)) 1065 zipMode = 1; 1066 if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2)) 1067 zipMode = 2; 1068#endif 1069 /* NB: writeTarFile() closes tar_handle->src_fd */ 1070 return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, 1071 tar_handle->accept, 1072 tar_handle->reject, zipMode); 1073 } 1074 1075 while (get_header_ptr(tar_handle) == EXIT_SUCCESS) 1076 continue; 1077 1078 /* Check that every file that should have been extracted was */ 1079 while (tar_handle->accept) { 1080 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) 1081 && !find_list_entry(tar_handle->passed, tar_handle->accept->data) 1082 ) { 1083 bb_error_msg_and_die("%s: not found in archive", 1084 tar_handle->accept->data); 1085 } 1086 tar_handle->accept = tar_handle->accept->link; 1087 } 1088 if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */) 1089 close(tar_handle->src_fd); 1090 1091 return EXIT_SUCCESS; 1092} 1093