1 /*- 2 * Copyright (c) 2003-2007 Tim Kientzle 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "bsdtar_platform.h" 27 __FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.91 2008/05/26 17:10:10 kientzle Exp $"); 28 29 #ifdef HAVE_SYS_PARAM_H 30 #include <sys/param.h> 31 #endif 32 #ifdef HAVE_SYS_STAT_H 33 #include <sys/stat.h> 34 #endif 35 #ifdef HAVE_ERRNO_H 36 #include <errno.h> 37 #endif 38 #ifdef HAVE_FCNTL_H 39 #include <fcntl.h> 40 #endif 41 #ifdef HAVE_GETOPT_LONG 42 #include <getopt.h> 43 #else 44 struct option { 45 const char *name; 46 int has_arg; 47 int *flag; 48 int val; 49 }; 50 #define no_argument 0 51 #define required_argument 1 52 #endif 53 #ifdef HAVE_LANGINFO_H 54 #include <langinfo.h> 55 #endif 56 #ifdef HAVE_LOCALE_H 57 #include <locale.h> 58 #endif 59 #ifdef HAVE_PATHS_H 60 #include <paths.h> 61 #endif 62 #include <stdio.h> 63 #ifdef HAVE_STDLIB_H 64 #include <stdlib.h> 65 #endif 66 #ifdef HAVE_STRING_H 67 #include <string.h> 68 #endif 69 #ifdef HAVE_TIME_H 70 #include <time.h> 71 #endif 72 #ifdef HAVE_UNISTD_H 73 #include <unistd.h> 74 #endif 75 #if HAVE_ZLIB_H 76 #include <zlib.h> 77 #endif 78 79 #include "bsdtar.h" 80 81 #if !HAVE_DECL_OPTARG 82 extern int optarg; 83 #endif 84 85 #if !HAVE_DECL_OPTIND 86 extern int optind; 87 #endif 88 89 /* 90 * Per POSIX.1-1988, tar defaults to reading/writing archives to/from 91 * the default tape device for the system. Pick something reasonable here. 92 */ 93 #ifdef __linux 94 #define _PATH_DEFTAPE "/dev/st0" 95 #endif 96 97 #ifndef _PATH_DEFTAPE 98 #define _PATH_DEFTAPE "/dev/tape" 99 #endif 100 101 /* External function to parse a date/time string (from getdate.y) */ 102 time_t get_date(const char *); 103 104 static int bsdtar_getopt(struct bsdtar *, const char *optstring, 105 const struct option **poption); 106 static void long_help(struct bsdtar *); 107 static void only_mode(struct bsdtar *, const char *opt, 108 const char *valid); 109 static char ** rewrite_argv(struct bsdtar *, 110 int *argc, char ** src_argv, 111 const char *optstring); 112 static void set_mode(struct bsdtar *, char opt); 113 static void version(void); 114 115 /* 116 * The leading '+' here forces the GNU version of getopt() (as well as 117 * both the GNU and BSD versions of getopt_long) to stop at the first 118 * non-option. Otherwise, GNU getopt() permutes the arguments and 119 * screws up -C processing. 120 */ 121 static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPprts:ST:UuvW:wX:xyZz"; 122 123 /* 124 * Most of these long options are deliberately not documented. They 125 * are provided only to make life easier for people who also use GNU tar. 126 * The only long options documented in the manual page are the ones 127 * with no corresponding short option, such as --exclude, --nodump, 128 * and --fast-read. 129 * 130 * On systems that lack getopt_long, long options can be specified 131 * using -W longopt and -W longopt=value, e.g. "-W nodump" is the same 132 * as "--nodump" and "-W exclude=pattern" is the same as "--exclude 133 * pattern". This does not rely the GNU getopt() "W;" extension, so 134 * should work correctly on any system with a POSIX-compliant getopt(). 135 */ 136 137 /* Fake short equivalents for long options that otherwise lack them. */ 138 enum { 139 OPTION_CHECK_LINKS = 1, 140 OPTION_CHROOT, 141 OPTION_EXCLUDE, 142 OPTION_FORMAT, 143 OPTION_HELP, 144 OPTION_INCLUDE, 145 OPTION_KEEP_NEWER_FILES, 146 OPTION_NEWER_CTIME, 147 OPTION_NEWER_CTIME_THAN, 148 OPTION_NEWER_MTIME, 149 OPTION_NEWER_MTIME_THAN, 150 OPTION_NODUMP, 151 OPTION_NO_SAME_OWNER, 152 OPTION_NO_SAME_PERMISSIONS, 153 OPTION_NULL, 154 OPTION_NUMERIC_OWNER, 155 OPTION_ONE_FILE_SYSTEM, 156 OPTION_POSIX, 157 OPTION_STRIP_COMPONENTS, 158 OPTION_TOTALS, 159 OPTION_USE_COMPRESS_PROGRAM, 160 OPTION_VERSION 161 }; 162 163 /* 164 * If you add anything, be very careful to keep this list properly 165 * sorted, as the -W logic relies on it. 166 */ 167 static const struct option tar_longopts[] = { 168 { "absolute-paths", no_argument, NULL, 'P' }, 169 { "append", no_argument, NULL, 'r' }, 170 { "block-size", required_argument, NULL, 'b' }, 171 { "bunzip2", no_argument, NULL, 'j' }, 172 { "bzip", no_argument, NULL, 'j' }, 173 { "bzip2", no_argument, NULL, 'j' }, 174 { "cd", required_argument, NULL, 'C' }, 175 { "check-links", no_argument, NULL, OPTION_CHECK_LINKS }, 176 { "chroot", no_argument, NULL, OPTION_CHROOT }, 177 { "compress", no_argument, NULL, 'Z' }, 178 { "confirmation", no_argument, NULL, 'w' }, 179 { "create", no_argument, NULL, 'c' }, 180 { "dereference", no_argument, NULL, 'L' }, 181 { "directory", required_argument, NULL, 'C' }, 182 { "exclude", required_argument, NULL, OPTION_EXCLUDE }, 183 { "exclude-from", required_argument, NULL, 'X' }, 184 { "extract", no_argument, NULL, 'x' }, 185 { "fast-read", no_argument, NULL, 'q' }, 186 { "file", required_argument, NULL, 'f' }, 187 { "files-from", required_argument, NULL, 'T' }, 188 { "format", required_argument, NULL, OPTION_FORMAT }, 189 { "gunzip", no_argument, NULL, 'z' }, 190 { "gzip", no_argument, NULL, 'z' }, 191 { "help", no_argument, NULL, OPTION_HELP }, 192 { "include", required_argument, NULL, OPTION_INCLUDE }, 193 { "interactive", no_argument, NULL, 'w' }, 194 { "insecure", no_argument, NULL, 'P' }, 195 { "keep-newer-files", no_argument, NULL, OPTION_KEEP_NEWER_FILES }, 196 { "keep-old-files", no_argument, NULL, 'k' }, 197 { "list", no_argument, NULL, 't' }, 198 { "modification-time", no_argument, NULL, 'm' }, 199 { "newer", required_argument, NULL, OPTION_NEWER_CTIME }, 200 { "newer-ctime", required_argument, NULL, OPTION_NEWER_CTIME }, 201 { "newer-ctime-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, 202 { "newer-mtime", required_argument, NULL, OPTION_NEWER_MTIME }, 203 { "newer-mtime-than", required_argument, NULL, OPTION_NEWER_MTIME_THAN }, 204 { "newer-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, 205 { "nodump", no_argument, NULL, OPTION_NODUMP }, 206 { "norecurse", no_argument, NULL, 'n' }, 207 { "no-recursion", no_argument, NULL, 'n' }, 208 { "no-same-owner", no_argument, NULL, OPTION_NO_SAME_OWNER }, 209 { "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS }, 210 { "null", no_argument, NULL, OPTION_NULL }, 211 { "numeric-owner", no_argument, NULL, OPTION_NUMERIC_OWNER }, 212 { "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM }, 213 { "posix", no_argument, NULL, OPTION_POSIX }, 214 { "preserve-permissions", no_argument, NULL, 'p' }, 215 { "read-full-blocks", no_argument, NULL, 'B' }, 216 { "same-permissions", no_argument, NULL, 'p' }, 217 { "strip-components", required_argument, NULL, OPTION_STRIP_COMPONENTS }, 218 { "to-stdout", no_argument, NULL, 'O' }, 219 { "totals", no_argument, NULL, OPTION_TOTALS }, 220 { "uncompress", no_argument, NULL, 'Z' }, 221 { "unlink", no_argument, NULL, 'U' }, 222 { "unlink-first", no_argument, NULL, 'U' }, 223 { "update", no_argument, NULL, 'u' }, 224 { "use-compress-program", 225 required_argument, NULL, OPTION_USE_COMPRESS_PROGRAM }, 226 { "verbose", no_argument, NULL, 'v' }, 227 { "version", no_argument, NULL, OPTION_VERSION }, 228 { NULL, 0, NULL, 0 } 229 }; 230 231 /* A basic set of security flags to request from libarchive. */ 232 #define SECURITY \ 233 (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ 234 | ARCHIVE_EXTRACT_SECURE_NODOTDOT) 235 236 int 237 main(int argc, char **argv) 238 { 239 struct bsdtar *bsdtar, bsdtar_storage; 240 const struct option *option; 241 int opt, t; 242 char option_o; 243 char possible_help_request; 244 char buff[16]; 245 246 /* 247 * Use a pointer for consistency, but stack-allocated storage 248 * for ease of cleanup. 249 */ 250 bsdtar = &bsdtar_storage; 251 memset(bsdtar, 0, sizeof(*bsdtar)); 252 bsdtar->fd = -1; /* Mark as "unused" */ 253 option_o = 0; 254 255 /* Need bsdtar->progname before calling bsdtar_warnc. */ 256 if (*argv == NULL) 257 bsdtar->progname = "bsdtar"; 258 else { 259 bsdtar->progname = strrchr(*argv, '/'); 260 if (bsdtar->progname != NULL) 261 bsdtar->progname++; 262 else 263 bsdtar->progname = *argv; 264 } 265 266 if (setlocale(LC_ALL, "") == NULL) 267 bsdtar_warnc(bsdtar, 0, "Failed to set default locale"); 268 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) 269 bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 270 #endif 271 possible_help_request = 0; 272 273 /* Look up uid of current user for future reference */ 274 bsdtar->user_uid = geteuid(); 275 276 /* Default: open tape drive. */ 277 bsdtar->filename = getenv("TAPE"); 278 if (bsdtar->filename == NULL) 279 bsdtar->filename = _PATH_DEFTAPE; 280 281 /* Default: preserve mod time on extract */ 282 bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME; 283 284 /* Default: Perform basic security checks. */ 285 bsdtar->extract_flags |= SECURITY; 286 287 /* Defaults for root user: */ 288 if (bsdtar->user_uid == 0) { 289 /* --same-owner */ 290 bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; 291 /* -p */ 292 bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 293 bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 294 bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 295 bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 296 } 297 298 /* Rewrite traditional-style tar arguments, if used. */ 299 argv = rewrite_argv(bsdtar, &argc, argv, tar_opts); 300 301 bsdtar->argv = argv; 302 bsdtar->argc = argc; 303 304 /* Process all remaining arguments now. */ 305 /* 306 * Comments following each option indicate where that option 307 * originated: SUSv2, POSIX, GNU tar, star, etc. If there's 308 * no such comment, then I don't know of anyone else who 309 * implements that option. 310 */ 311 while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) { 312 switch (opt) { 313 case 'B': /* GNU tar */ 314 /* libarchive doesn't need this; just ignore it. */ 315 break; 316 case 'b': /* SUSv2 */ 317 t = atoi(optarg); 318 if (t <= 0 || t > 1024) 319 bsdtar_errc(bsdtar, 1, 0, 320 "Argument to -b is out of range (1..1024)"); 321 bsdtar->bytes_per_block = 512 * t; 322 break; 323 case 'C': /* GNU tar */ 324 set_chdir(bsdtar, optarg); 325 break; 326 case 'c': /* SUSv2 */ 327 set_mode(bsdtar, opt); 328 break; 329 case OPTION_CHECK_LINKS: /* GNU tar */ 330 bsdtar->option_warn_links = 1; 331 break; 332 case OPTION_CHROOT: /* NetBSD */ 333 bsdtar->option_chroot = 1; 334 break; 335 case OPTION_EXCLUDE: /* GNU tar */ 336 if (exclude(bsdtar, optarg)) 337 bsdtar_errc(bsdtar, 1, 0, 338 "Couldn't exclude %s\n", optarg); 339 break; 340 case OPTION_FORMAT: /* GNU tar, others */ 341 bsdtar->create_format = optarg; 342 break; 343 case 'f': /* SUSv2 */ 344 bsdtar->filename = optarg; 345 if (strcmp(bsdtar->filename, "-") == 0) 346 bsdtar->filename = NULL; 347 break; 348 case 'H': /* BSD convention */ 349 bsdtar->symlink_mode = 'H'; 350 break; 351 case 'h': /* Linux Standards Base, gtar; synonym for -L */ 352 bsdtar->symlink_mode = 'L'; 353 /* Hack: -h by itself is the "help" command. */ 354 possible_help_request = 1; 355 break; 356 case OPTION_HELP: /* GNU tar, others */ 357 long_help(bsdtar); 358 exit(0); 359 break; 360 case 'I': /* GNU tar */ 361 /* 362 * TODO: Allow 'names' to come from an archive, 363 * not just a text file. Design a good UI for 364 * allowing names and mode/owner to be read 365 * from an archive, with contents coming from 366 * disk. This can be used to "refresh" an 367 * archive or to design archives with special 368 * permissions without having to create those 369 * permissions on disk. 370 */ 371 bsdtar->names_from_file = optarg; 372 break; 373 case OPTION_INCLUDE: 374 /* 375 * Noone else has the @archive extension, so 376 * noone else needs this to filter entries 377 * when transforming archives. 378 */ 379 if (include(bsdtar, optarg)) 380 bsdtar_errc(bsdtar, 1, 0, 381 "Failed to add %s to inclusion list", 382 optarg); 383 break; 384 case 'j': /* GNU tar */ 385 #if HAVE_LIBBZ2 386 if (bsdtar->create_compression != '\0') 387 bsdtar_errc(bsdtar, 1, 0, 388 "Can't specify both -%c and -%c", opt, 389 bsdtar->create_compression); 390 bsdtar->create_compression = opt; 391 #else 392 bsdtar_warnc(bsdtar, 0, "-j compression not supported by this version of bsdtar"); 393 usage(bsdtar); 394 #endif 395 break; 396 case 'k': /* GNU tar */ 397 bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; 398 break; 399 case OPTION_KEEP_NEWER_FILES: /* GNU tar */ 400 bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; 401 break; 402 case 'L': /* BSD convention */ 403 bsdtar->symlink_mode = 'L'; 404 break; 405 case 'l': /* SUSv2 and GNU tar beginning with 1.16 */ 406 /* GNU tar 1.13 used -l for --one-file-system */ 407 bsdtar->option_warn_links = 1; 408 break; 409 case 'm': /* SUSv2 */ 410 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; 411 break; 412 case 'n': /* GNU tar */ 413 bsdtar->option_no_subdirs = 1; 414 break; 415 /* 416 * Selecting files by time: 417 * --newer-?time='date' Only files newer than 'date' 418 * --newer-?time-than='file' Only files newer than time 419 * on specified file (useful for incremental backups) 420 * TODO: Add corresponding "older" options to reverse these. 421 */ 422 case OPTION_NEWER_CTIME: /* GNU tar */ 423 bsdtar->newer_ctime_sec = get_date(optarg); 424 break; 425 case OPTION_NEWER_CTIME_THAN: 426 { 427 struct stat st; 428 if (stat(optarg, &st) != 0) 429 bsdtar_errc(bsdtar, 1, 0, 430 "Can't open file %s", optarg); 431 bsdtar->newer_ctime_sec = st.st_ctime; 432 bsdtar->newer_ctime_nsec = 433 ARCHIVE_STAT_CTIME_NANOS(&st); 434 } 435 break; 436 case OPTION_NEWER_MTIME: /* GNU tar */ 437 bsdtar->newer_mtime_sec = get_date(optarg); 438 break; 439 case OPTION_NEWER_MTIME_THAN: 440 { 441 struct stat st; 442 if (stat(optarg, &st) != 0) 443 bsdtar_errc(bsdtar, 1, 0, 444 "Can't open file %s", optarg); 445 bsdtar->newer_mtime_sec = st.st_mtime; 446 bsdtar->newer_mtime_nsec = 447 ARCHIVE_STAT_MTIME_NANOS(&st); 448 } 449 break; 450 case OPTION_NODUMP: /* star */ 451 bsdtar->option_honor_nodump = 1; 452 break; 453 case OPTION_NO_SAME_OWNER: /* GNU tar */ 454 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 455 break; 456 case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */ 457 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM; 458 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; 459 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; 460 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; 461 break; 462 case OPTION_NULL: /* GNU tar */ 463 bsdtar->option_null++; 464 break; 465 case OPTION_NUMERIC_OWNER: /* GNU tar */ 466 bsdtar->option_numeric_owner++; 467 break; 468 case 'O': /* GNU tar */ 469 bsdtar->option_stdout = 1; 470 break; 471 case 'o': /* SUSv2 and GNU conflict here, but not fatally */ 472 option_o = 1; /* Record it and resolve it later. */ 473 break; 474 case OPTION_ONE_FILE_SYSTEM: /* GNU tar */ 475 bsdtar->option_dont_traverse_mounts = 1; 476 break; 477 #if 0 478 /* 479 * The common BSD -P option is not necessary, since 480 * our default is to archive symlinks, not follow 481 * them. This is convenient, as -P conflicts with GNU 482 * tar anyway. 483 */ 484 case 'P': /* BSD convention */ 485 /* Default behavior, no option necessary. */ 486 break; 487 #endif 488 case 'P': /* GNU tar */ 489 bsdtar->extract_flags &= ~SECURITY; 490 bsdtar->option_absolute_paths = 1; 491 break; 492 case 'p': /* GNU tar, star */ 493 bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 494 bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 495 bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 496 bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 497 break; 498 case OPTION_POSIX: /* GNU tar */ 499 bsdtar->create_format = "pax"; 500 break; 501 case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */ 502 bsdtar->option_fast_read = 1; 503 break; 504 case 'r': /* SUSv2 */ 505 set_mode(bsdtar, opt); 506 break; 507 case 'S': /* NetBSD pax-as-tar */ 508 bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE; 509 break; 510 case 's': /* NetBSD pax-as-tar */ 511 #if HAVE_REGEX_H 512 add_substitution(bsdtar, optarg); 513 #else 514 bsdtar_warnc(bsdtar, 0, "-s is not supported by this version of bsdtar"); 515 usage(bsdtar); 516 #endif 517 break; 518 case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ 519 bsdtar->strip_components = atoi(optarg); 520 break; 521 case 'T': /* GNU tar */ 522 bsdtar->names_from_file = optarg; 523 break; 524 case 't': /* SUSv2 */ 525 set_mode(bsdtar, opt); 526 bsdtar->verbose++; 527 break; 528 case OPTION_TOTALS: /* GNU tar */ 529 bsdtar->option_totals++; 530 break; 531 case 'U': /* GNU tar */ 532 bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK; 533 bsdtar->option_unlink_first = 1; 534 break; 535 case 'u': /* SUSv2 */ 536 set_mode(bsdtar, opt); 537 break; 538 case 'v': /* SUSv2 */ 539 bsdtar->verbose++; 540 break; 541 case OPTION_VERSION: /* GNU convention */ 542 version(); 543 break; 544 #if 0 545 /* 546 * The -W longopt feature is handled inside of 547 * bsdtar_getop(), so -W is not available here. 548 */ 549 case 'W': /* Obscure, but useful GNU convention. */ 550 break; 551 #endif 552 case 'w': /* SUSv2 */ 553 bsdtar->option_interactive = 1; 554 break; 555 case 'X': /* GNU tar */ 556 if (exclude_from_file(bsdtar, optarg)) 557 bsdtar_errc(bsdtar, 1, 0, 558 "failed to process exclusions from file %s", 559 optarg); 560 break; 561 case 'x': /* SUSv2 */ 562 set_mode(bsdtar, opt); 563 break; 564 case 'y': /* FreeBSD version of GNU tar */ 565 #if HAVE_LIBBZ2 566 if (bsdtar->create_compression != '\0') 567 bsdtar_errc(bsdtar, 1, 0, 568 "Can't specify both -%c and -%c", opt, 569 bsdtar->create_compression); 570 bsdtar->create_compression = opt; 571 #else 572 bsdtar_warnc(bsdtar, 0, "-y compression not supported by this version of bsdtar"); 573 usage(bsdtar); 574 #endif 575 break; 576 case 'Z': /* GNU tar */ 577 if (bsdtar->create_compression != '\0') 578 bsdtar_errc(bsdtar, 1, 0, 579 "Can't specify both -%c and -%c", opt, 580 bsdtar->create_compression); 581 bsdtar->create_compression = opt; 582 break; 583 case 'z': /* GNU tar, star, many others */ 584 #if HAVE_LIBZ 585 if (bsdtar->create_compression != '\0') 586 bsdtar_errc(bsdtar, 1, 0, 587 "Can't specify both -%c and -%c", opt, 588 bsdtar->create_compression); 589 bsdtar->create_compression = opt; 590 #else 591 bsdtar_warnc(bsdtar, 0, "-z compression not supported by this version of bsdtar"); 592 usage(bsdtar); 593 #endif 594 break; 595 case OPTION_USE_COMPRESS_PROGRAM: 596 bsdtar->compress_program = optarg; 597 break; 598 default: 599 usage(bsdtar); 600 } 601 } 602 603 /* 604 * Sanity-check options. 605 */ 606 607 /* If no "real" mode was specified, treat -h as --help. */ 608 if ((bsdtar->mode == '\0') && possible_help_request) { 609 long_help(bsdtar); 610 exit(0); 611 } 612 613 /* Otherwise, a mode is required. */ 614 if (bsdtar->mode == '\0') 615 bsdtar_errc(bsdtar, 1, 0, 616 "Must specify one of -c, -r, -t, -u, -x"); 617 618 /* Check boolean options only permitted in certain modes. */ 619 if (bsdtar->option_dont_traverse_mounts) 620 only_mode(bsdtar, "--one-file-system", "cru"); 621 if (bsdtar->option_fast_read) 622 only_mode(bsdtar, "--fast-read", "xt"); 623 if (bsdtar->option_honor_nodump) 624 only_mode(bsdtar, "--nodump", "cru"); 625 if (option_o > 0) { 626 switch (bsdtar->mode) { 627 case 'c': 628 /* 629 * In GNU tar, -o means "old format." The 630 * "ustar" format is the closest thing 631 * supported by libarchive. 632 */ 633 bsdtar->create_format = "ustar"; 634 /* TODO: bsdtar->create_format = "v7"; */ 635 break; 636 case 'x': 637 /* POSIX-compatible behavior. */ 638 bsdtar->option_no_owner = 1; 639 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 640 break; 641 default: 642 only_mode(bsdtar, "-o", "xc"); 643 break; 644 } 645 } 646 if (bsdtar->option_no_subdirs) 647 only_mode(bsdtar, "-n", "cru"); 648 if (bsdtar->option_stdout) 649 only_mode(bsdtar, "-O", "xt"); 650 if (bsdtar->option_unlink_first) 651 only_mode(bsdtar, "-U", "x"); 652 if (bsdtar->option_warn_links) 653 only_mode(bsdtar, "--check-links", "cr"); 654 655 /* Check other parameters only permitted in certain modes. */ 656 if (bsdtar->create_compression != '\0') { 657 strcpy(buff, "-?"); 658 buff[1] = bsdtar->create_compression; 659 only_mode(bsdtar, buff, "cxt"); 660 } 661 if (bsdtar->create_format != NULL) 662 only_mode(bsdtar, "--format", "cru"); 663 if (bsdtar->symlink_mode != '\0') { 664 strcpy(buff, "-?"); 665 buff[1] = bsdtar->symlink_mode; 666 only_mode(bsdtar, buff, "cru"); 667 } 668 if (bsdtar->strip_components != 0) 669 only_mode(bsdtar, "--strip-components", "xt"); 670 671 bsdtar->argc -= optind; 672 bsdtar->argv += optind; 673 674 switch(bsdtar->mode) { 675 case 'c': 676 tar_mode_c(bsdtar); 677 break; 678 case 'r': 679 tar_mode_r(bsdtar); 680 break; 681 case 't': 682 tar_mode_t(bsdtar); 683 break; 684 case 'u': 685 tar_mode_u(bsdtar); 686 break; 687 case 'x': 688 tar_mode_x(bsdtar); 689 break; 690 } 691 692 cleanup_exclusions(bsdtar); 693 #if HAVE_REGEX_H 694 cleanup_substitution(bsdtar); 695 #endif 696 697 if (bsdtar->return_value != 0) 698 bsdtar_warnc(bsdtar, 0, 699 "Error exit delayed from previous errors."); 700 return (bsdtar->return_value); 701 } 702 703 static void 704 set_mode(struct bsdtar *bsdtar, char opt) 705 { 706 if (bsdtar->mode != '\0' && bsdtar->mode != opt) 707 bsdtar_errc(bsdtar, 1, 0, 708 "Can't specify both -%c and -%c", opt, bsdtar->mode); 709 bsdtar->mode = opt; 710 } 711 712 /* 713 * Verify that the mode is correct. 714 */ 715 static void 716 only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) 717 { 718 if (strchr(valid_modes, bsdtar->mode) == NULL) 719 bsdtar_errc(bsdtar, 1, 0, 720 "Option %s is not permitted in mode -%c", 721 opt, bsdtar->mode); 722 } 723 724 725 /*- 726 * Convert traditional tar arguments into new-style. 727 * For example, 728 * tar tvfb file.tar 32 --exclude FOO 729 * will be converted to 730 * tar -t -v -f file.tar -b 32 --exclude FOO 731 * 732 * This requires building a new argv array. The initial bundled word 733 * gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0". 734 * The new argv array has pointers into this string intermingled with 735 * pointers to the existing arguments. Arguments are moved to 736 * immediately follow their options. 737 * 738 * The optstring argument here is the same one passed to getopt(3). 739 * It is used to determine which option letters have trailing arguments. 740 */ 741 char ** 742 rewrite_argv(struct bsdtar *bsdtar, int *argc, char **src_argv, 743 const char *optstring) 744 { 745 char **new_argv, **dest_argv; 746 const char *p; 747 char *src, *dest; 748 749 if (src_argv[0] == NULL || src_argv[1] == NULL || 750 src_argv[1][0] == '-' || src_argv[1][0] == '\0') 751 return (src_argv); 752 753 *argc += strlen(src_argv[1]) - 1; 754 new_argv = malloc((*argc + 1) * sizeof(new_argv[0])); 755 if (new_argv == NULL) 756 bsdtar_errc(bsdtar, 1, errno, "No Memory"); 757 758 dest_argv = new_argv; 759 *dest_argv++ = *src_argv++; 760 761 dest = malloc(strlen(*src_argv) * 3); 762 if (dest == NULL) 763 bsdtar_errc(bsdtar, 1, errno, "No memory"); 764 for (src = *src_argv++; *src != '\0'; src++) { 765 *dest_argv++ = dest; 766 *dest++ = '-'; 767 *dest++ = *src; 768 *dest++ = '\0'; 769 /* If option takes an argument, insert that into the list. */ 770 for (p = optstring; p != NULL && *p != '\0'; p++) { 771 if (*p != *src) 772 continue; 773 if (p[1] != ':') /* No arg required, done. */ 774 break; 775 if (*src_argv == NULL) /* No arg available? Error. */ 776 bsdtar_errc(bsdtar, 1, 0, 777 "Option %c requires an argument", 778 *src); 779 *dest_argv++ = *src_argv++; 780 break; 781 } 782 } 783 784 /* Copy remaining arguments, including trailing NULL. */ 785 while ((*dest_argv++ = *src_argv++) != NULL) 786 ; 787 788 return (new_argv); 789 } 790 791 void 792 usage(struct bsdtar *bsdtar) 793 { 794 const char *p; 795 796 p = bsdtar->progname; 797 798 fprintf(stderr, "Usage:\n"); 799 fprintf(stderr, " List: %s -tf <archive-filename>\n", p); 800 fprintf(stderr, " Extract: %s -xf <archive-filename>\n", p); 801 fprintf(stderr, " Create: %s -cf <archive-filename> [filenames...]\n", p); 802 #ifdef HAVE_GETOPT_LONG 803 fprintf(stderr, " Help: %s --help\n", p); 804 #else 805 fprintf(stderr, " Help: %s -h\n", p); 806 #endif 807 exit(1); 808 } 809 810 static void 811 version(void) 812 { 813 printf("bsdtar %s - %s\n", 814 BSDTAR_VERSION_STRING, 815 archive_version()); 816 exit(0); 817 } 818 819 static const char *long_help_msg = 820 "First option must be a mode specifier:\n" 821 " -c Create -r Add/Replace -t List -u Update -x Extract\n" 822 "Common Options:\n" 823 " -b # Use # 512-byte records per I/O block\n" 824 " -f <filename> Location of archive (default " _PATH_DEFTAPE ")\n" 825 " -v Verbose\n" 826 " -w Interactive\n" 827 "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n" 828 " <file>, <dir> add these items to archive\n" 829 " -z, -j Compress archive with gzip/bzip2\n" 830 " --format {ustar|pax|cpio|shar} Select archive format\n" 831 #ifdef HAVE_GETOPT_LONG 832 " --exclude <pattern> Skip files that match pattern\n" 833 #else 834 " -W exclude=<pattern> Skip files that match pattern\n" 835 #endif 836 " -C <dir> Change to <dir> before processing remaining files\n" 837 " @<archive> Add entries from <archive> to output\n" 838 "List: %p -t [options] [<patterns>]\n" 839 " <patterns> If specified, list only entries that match\n" 840 "Extract: %p -x [options] [<patterns>]\n" 841 " <patterns> If specified, extract only entries that match\n" 842 " -k Keep (don't overwrite) existing files\n" 843 " -m Don't restore modification times\n" 844 " -O Write entries to stdout, don't restore to disk\n" 845 " -p Restore permissions (including ACLs, owner, file flags)\n"; 846 847 848 /* 849 * Note that the word 'bsdtar' will always appear in the first line 850 * of output. 851 * 852 * In particular, /bin/sh scripts that need to test for the presence 853 * of bsdtar can use the following template: 854 * 855 * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \ 856 * echo bsdtar; else echo not bsdtar; fi 857 */ 858 static void 859 long_help(struct bsdtar *bsdtar) 860 { 861 const char *prog; 862 const char *p; 863 864 prog = bsdtar->progname; 865 866 fflush(stderr); 867 868 p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : ""; 869 printf("%s%s: manipulate archive files\n", prog, p); 870 871 for (p = long_help_msg; *p != '\0'; p++) { 872 if (*p == '%') { 873 if (p[1] == 'p') { 874 fputs(prog, stdout); 875 p++; 876 } else 877 putchar('%'); 878 } else 879 putchar(*p); 880 } 881 version(); 882 } 883 884 static int 885 bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring, 886 const struct option **poption) 887 { 888 char *p, *q; 889 const struct option *option; 890 int opt; 891 int option_index; 892 size_t option_length; 893 894 option_index = -1; 895 *poption = NULL; 896 897 #ifdef HAVE_GETOPT_LONG 898 opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring, 899 tar_longopts, &option_index); 900 if (option_index > -1) 901 *poption = tar_longopts + option_index; 902 #else 903 opt = getopt(bsdtar->argc, bsdtar->argv, optstring); 904 #endif 905 906 /* Support long options through -W longopt=value */ 907 if (opt == 'W') { 908 p = optarg; 909 q = strchr(optarg, '='); 910 if (q != NULL) { 911 option_length = (size_t)(q - p); 912 optarg = q + 1; 913 } else { 914 option_length = strlen(p); 915 optarg = NULL; 916 } 917 option = tar_longopts; 918 while (option->name != NULL && 919 (strlen(option->name) < option_length || 920 strncmp(p, option->name, option_length) != 0 )) { 921 option++; 922 } 923 924 if (option->name != NULL) { 925 *poption = option; 926 opt = option->val; 927 928 /* If the first match was exact, we're done. */ 929 if (strncmp(p, option->name, strlen(option->name)) == 0) { 930 while (option->name != NULL) 931 option++; 932 } else { 933 /* Check if there's another match. */ 934 option++; 935 while (option->name != NULL && 936 (strlen(option->name) < option_length || 937 strncmp(p, option->name, option_length) != 0)) { 938 option++; 939 } 940 } 941 if (option->name != NULL) 942 bsdtar_errc(bsdtar, 1, 0, 943 "Ambiguous option %s " 944 "(matches both %s and %s)", 945 p, (*poption)->name, option->name); 946 947 if ((*poption)->has_arg == required_argument 948 && optarg == NULL) 949 bsdtar_errc(bsdtar, 1, 0, 950 "Option \"%s\" requires argument", p); 951 } else { 952 opt = '?'; 953 /* TODO: Set up a fake 'struct option' for 954 * error reporting... ? ? ? */ 955 } 956 } 957 958 return (opt); 959 } 960