bsdtar.c revision 228753
1228753Smm/*- 2228753Smm * Copyright (c) 2003-2008 Tim Kientzle 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm#include "bsdtar_platform.h" 27228753Smm__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.93 2008/11/08 04:43:24 kientzle Exp $"); 28228753Smm 29228753Smm#ifdef HAVE_SYS_PARAM_H 30228753Smm#include <sys/param.h> 31228753Smm#endif 32228753Smm#ifdef HAVE_SYS_STAT_H 33228753Smm#include <sys/stat.h> 34228753Smm#endif 35228753Smm#ifdef HAVE_ERRNO_H 36228753Smm#include <errno.h> 37228753Smm#endif 38228753Smm#ifdef HAVE_FCNTL_H 39228753Smm#include <fcntl.h> 40228753Smm#endif 41228753Smm#ifdef HAVE_LANGINFO_H 42228753Smm#include <langinfo.h> 43228753Smm#endif 44228753Smm#ifdef HAVE_LOCALE_H 45228753Smm#include <locale.h> 46228753Smm#endif 47228753Smm#ifdef HAVE_PATHS_H 48228753Smm#include <paths.h> 49228753Smm#endif 50228753Smm#ifdef HAVE_SIGNAL_H 51228753Smm#include <signal.h> 52228753Smm#endif 53228753Smm#include <stdio.h> 54228753Smm#ifdef HAVE_STDLIB_H 55228753Smm#include <stdlib.h> 56228753Smm#endif 57228753Smm#ifdef HAVE_STRING_H 58228753Smm#include <string.h> 59228753Smm#endif 60228753Smm#ifdef HAVE_TIME_H 61228753Smm#include <time.h> 62228753Smm#endif 63228753Smm#ifdef HAVE_UNISTD_H 64228753Smm#include <unistd.h> 65228753Smm#endif 66228753Smm#if HAVE_ZLIB_H 67228753Smm#include <zlib.h> 68228753Smm#endif 69228753Smm 70228753Smm#include "bsdtar.h" 71228753Smm#include "err.h" 72228753Smm 73228753Smm/* 74228753Smm * Per POSIX.1-1988, tar defaults to reading/writing archives to/from 75228753Smm * the default tape device for the system. Pick something reasonable here. 76228753Smm */ 77228753Smm#ifdef __linux 78228753Smm#define _PATH_DEFTAPE "/dev/st0" 79228753Smm#endif 80228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 81228753Smm#define _PATH_DEFTAPE "\\\\.\\tape0" 82228753Smm#endif 83228753Smm 84228753Smm#ifndef _PATH_DEFTAPE 85228753Smm#define _PATH_DEFTAPE "/dev/tape" 86228753Smm#endif 87228753Smm 88228753Smm#ifdef __MINGW32__ 89228753Smmint _CRT_glob = 0; /* Disable broken CRT globbing. */ 90228753Smm#endif 91228753Smm 92228753Smmstatic struct bsdtar *_bsdtar; 93228753Smm 94228753Smm#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) 95228753Smmstatic volatile int siginfo_occurred; 96228753Smm 97228753Smmstatic void 98228753Smmsiginfo_handler(int sig) 99228753Smm{ 100228753Smm (void)sig; /* UNUSED */ 101228753Smm siginfo_occurred = 1; 102228753Smm} 103228753Smm 104228753Smmint 105228753Smmneed_report(void) 106228753Smm{ 107228753Smm int r = siginfo_occurred; 108228753Smm siginfo_occurred = 0; 109228753Smm return (r); 110228753Smm} 111228753Smm#else 112228753Smmint 113228753Smmneed_report(void) 114228753Smm{ 115228753Smm return (0); 116228753Smm} 117228753Smm#endif 118228753Smm 119228753Smm/* External function to parse a date/time string */ 120228753Smmtime_t get_date(time_t, const char *); 121228753Smm 122228753Smmstatic void long_help(void); 123228753Smmstatic void only_mode(struct bsdtar *, const char *opt, 124228753Smm const char *valid); 125228753Smmstatic void set_mode(struct bsdtar *, char opt); 126228753Smmstatic void version(void); 127228753Smm 128228753Smm/* A basic set of security flags to request from libarchive. */ 129228753Smm#define SECURITY \ 130228753Smm (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ 131228753Smm | ARCHIVE_EXTRACT_SECURE_NODOTDOT) 132228753Smm 133228753Smmint 134228753Smmmain(int argc, char **argv) 135228753Smm{ 136228753Smm struct bsdtar *bsdtar, bsdtar_storage; 137228753Smm int opt, t; 138228753Smm char option_o; 139228753Smm char possible_help_request; 140228753Smm char buff[16]; 141228753Smm time_t now; 142228753Smm 143228753Smm /* 144228753Smm * Use a pointer for consistency, but stack-allocated storage 145228753Smm * for ease of cleanup. 146228753Smm */ 147228753Smm _bsdtar = bsdtar = &bsdtar_storage; 148228753Smm memset(bsdtar, 0, sizeof(*bsdtar)); 149228753Smm bsdtar->fd = -1; /* Mark as "unused" */ 150228753Smm bsdtar->gid = -1; 151228753Smm bsdtar->uid = -1; 152228753Smm option_o = 0; 153228753Smm 154228753Smm#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) 155228753Smm { /* Catch SIGINFO and SIGUSR1, if they exist. */ 156228753Smm struct sigaction sa; 157228753Smm sa.sa_handler = siginfo_handler; 158228753Smm sigemptyset(&sa.sa_mask); 159228753Smm sa.sa_flags = 0; 160228753Smm#ifdef SIGINFO 161228753Smm if (sigaction(SIGINFO, &sa, NULL)) 162228753Smm lafe_errc(1, errno, "sigaction(SIGINFO) failed"); 163228753Smm#endif 164228753Smm#ifdef SIGUSR1 165228753Smm /* ... and treat SIGUSR1 the same way as SIGINFO. */ 166228753Smm if (sigaction(SIGUSR1, &sa, NULL)) 167228753Smm lafe_errc(1, errno, "sigaction(SIGUSR1) failed"); 168228753Smm#endif 169228753Smm } 170228753Smm#endif 171228753Smm 172228753Smm 173228753Smm /* Need lafe_progname before calling lafe_warnc. */ 174228753Smm if (*argv == NULL) 175228753Smm lafe_progname = "bsdtar"; 176228753Smm else { 177228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 178228753Smm lafe_progname = strrchr(*argv, '\\'); 179228753Smm#else 180228753Smm lafe_progname = strrchr(*argv, '/'); 181228753Smm#endif 182228753Smm if (lafe_progname != NULL) 183228753Smm lafe_progname++; 184228753Smm else 185228753Smm lafe_progname = *argv; 186228753Smm } 187228753Smm 188228753Smm time(&now); 189228753Smm 190228753Smm#if HAVE_SETLOCALE 191228753Smm if (setlocale(LC_ALL, "") == NULL) 192228753Smm lafe_warnc(0, "Failed to set default locale"); 193228753Smm#endif 194228753Smm#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) 195228753Smm bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 196228753Smm#endif 197228753Smm possible_help_request = 0; 198228753Smm 199228753Smm /* Look up uid of current user for future reference */ 200228753Smm bsdtar->user_uid = geteuid(); 201228753Smm 202228753Smm /* Default: open tape drive. */ 203228753Smm bsdtar->filename = getenv("TAPE"); 204228753Smm if (bsdtar->filename == NULL) 205228753Smm bsdtar->filename = _PATH_DEFTAPE; 206228753Smm 207228753Smm /* Default: preserve mod time on extract */ 208228753Smm bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME; 209228753Smm 210228753Smm /* Default: Perform basic security checks. */ 211228753Smm bsdtar->extract_flags |= SECURITY; 212228753Smm 213228753Smm#ifndef _WIN32 214228753Smm /* On POSIX systems, assume --same-owner and -p when run by 215228753Smm * the root user. This doesn't make any sense on Windows. */ 216228753Smm if (bsdtar->user_uid == 0) { 217228753Smm /* --same-owner */ 218228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; 219228753Smm /* -p */ 220228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 221228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 222228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 223228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 224228753Smm } 225228753Smm#endif 226228753Smm 227228753Smm bsdtar->argv = argv; 228228753Smm bsdtar->argc = argc; 229228753Smm 230228753Smm /* 231228753Smm * Comments following each option indicate where that option 232228753Smm * originated: SUSv2, POSIX, GNU tar, star, etc. If there's 233228753Smm * no such comment, then I don't know of anyone else who 234228753Smm * implements that option. 235228753Smm */ 236228753Smm while ((opt = bsdtar_getopt(bsdtar)) != -1) { 237228753Smm switch (opt) { 238228753Smm case 'B': /* GNU tar */ 239228753Smm /* libarchive doesn't need this; just ignore it. */ 240228753Smm break; 241228753Smm case 'b': /* SUSv2 */ 242228753Smm t = atoi(bsdtar->optarg); 243228753Smm if (t <= 0 || t > 8192) 244228753Smm lafe_errc(1, 0, 245228753Smm "Argument to -b is out of range (1..8192)"); 246228753Smm bsdtar->bytes_per_block = 512 * t; 247228753Smm break; 248228753Smm case 'C': /* GNU tar */ 249228753Smm set_chdir(bsdtar, bsdtar->optarg); 250228753Smm break; 251228753Smm case 'c': /* SUSv2 */ 252228753Smm set_mode(bsdtar, opt); 253228753Smm break; 254228753Smm case OPTION_CHECK_LINKS: /* GNU tar */ 255228753Smm bsdtar->option_warn_links = 1; 256228753Smm break; 257228753Smm case OPTION_CHROOT: /* NetBSD */ 258228753Smm bsdtar->option_chroot = 1; 259228753Smm break; 260228753Smm case OPTION_EXCLUDE: /* GNU tar */ 261228753Smm if (lafe_exclude(&bsdtar->matching, bsdtar->optarg)) 262228753Smm lafe_errc(1, 0, 263228753Smm "Couldn't exclude %s\n", bsdtar->optarg); 264228753Smm break; 265228753Smm case OPTION_FORMAT: /* GNU tar, others */ 266228753Smm bsdtar->create_format = bsdtar->optarg; 267228753Smm break; 268228753Smm case 'f': /* SUSv2 */ 269228753Smm bsdtar->filename = bsdtar->optarg; 270228753Smm if (strcmp(bsdtar->filename, "-") == 0) 271228753Smm bsdtar->filename = NULL; 272228753Smm break; 273228753Smm case OPTION_GID: /* cpio */ 274228753Smm t = atoi(bsdtar->optarg); 275228753Smm if (t < 0) 276228753Smm lafe_errc(1, 0, 277228753Smm "Argument to --gid must be positive"); 278228753Smm bsdtar->gid = t; 279228753Smm break; 280228753Smm case OPTION_GNAME: /* cpio */ 281228753Smm bsdtar->gname = bsdtar->optarg; 282228753Smm break; 283228753Smm case 'H': /* BSD convention */ 284228753Smm bsdtar->symlink_mode = 'H'; 285228753Smm break; 286228753Smm case 'h': /* Linux Standards Base, gtar; synonym for -L */ 287228753Smm bsdtar->symlink_mode = 'L'; 288228753Smm /* Hack: -h by itself is the "help" command. */ 289228753Smm possible_help_request = 1; 290228753Smm break; 291228753Smm case OPTION_HELP: /* GNU tar, others */ 292228753Smm long_help(); 293228753Smm exit(0); 294228753Smm break; 295228753Smm case 'I': /* GNU tar */ 296228753Smm /* 297228753Smm * TODO: Allow 'names' to come from an archive, 298228753Smm * not just a text file. Design a good UI for 299228753Smm * allowing names and mode/owner to be read 300228753Smm * from an archive, with contents coming from 301228753Smm * disk. This can be used to "refresh" an 302228753Smm * archive or to design archives with special 303228753Smm * permissions without having to create those 304228753Smm * permissions on disk. 305228753Smm */ 306228753Smm bsdtar->names_from_file = bsdtar->optarg; 307228753Smm break; 308228753Smm case OPTION_INCLUDE: 309228753Smm /* 310228753Smm * Noone else has the @archive extension, so 311228753Smm * noone else needs this to filter entries 312228753Smm * when transforming archives. 313228753Smm */ 314228753Smm if (lafe_include(&bsdtar->matching, bsdtar->optarg)) 315228753Smm lafe_errc(1, 0, 316228753Smm "Failed to add %s to inclusion list", 317228753Smm bsdtar->optarg); 318228753Smm break; 319228753Smm case 'j': /* GNU tar */ 320228753Smm if (bsdtar->create_compression != '\0') 321228753Smm lafe_errc(1, 0, 322228753Smm "Can't specify both -%c and -%c", opt, 323228753Smm bsdtar->create_compression); 324228753Smm bsdtar->create_compression = opt; 325228753Smm break; 326228753Smm case 'J': /* GNU tar 1.21 and later */ 327228753Smm if (bsdtar->create_compression != '\0') 328228753Smm lafe_errc(1, 0, 329228753Smm "Can't specify both -%c and -%c", opt, 330228753Smm bsdtar->create_compression); 331228753Smm bsdtar->create_compression = opt; 332228753Smm break; 333228753Smm case 'k': /* GNU tar */ 334228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; 335228753Smm break; 336228753Smm case OPTION_KEEP_NEWER_FILES: /* GNU tar */ 337228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; 338228753Smm break; 339228753Smm case 'L': /* BSD convention */ 340228753Smm bsdtar->symlink_mode = 'L'; 341228753Smm break; 342228753Smm case 'l': /* SUSv2 and GNU tar beginning with 1.16 */ 343228753Smm /* GNU tar 1.13 used -l for --one-file-system */ 344228753Smm bsdtar->option_warn_links = 1; 345228753Smm break; 346228753Smm case OPTION_LZMA: 347228753Smm if (bsdtar->create_compression != '\0') 348228753Smm lafe_errc(1, 0, 349228753Smm "Can't specify both -%c and -%c", opt, 350228753Smm bsdtar->create_compression); 351228753Smm bsdtar->create_compression = opt; 352228753Smm break; 353228753Smm case 'm': /* SUSv2 */ 354228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; 355228753Smm break; 356228753Smm case 'n': /* GNU tar */ 357228753Smm bsdtar->option_no_subdirs = 1; 358228753Smm break; 359228753Smm /* 360228753Smm * Selecting files by time: 361228753Smm * --newer-?time='date' Only files newer than 'date' 362228753Smm * --newer-?time-than='file' Only files newer than time 363228753Smm * on specified file (useful for incremental backups) 364228753Smm * TODO: Add corresponding "older" options to reverse these. 365228753Smm */ 366228753Smm case OPTION_NEWER_CTIME: /* GNU tar */ 367228753Smm bsdtar->newer_ctime_sec = get_date(now, bsdtar->optarg); 368228753Smm break; 369228753Smm case OPTION_NEWER_CTIME_THAN: 370228753Smm { 371228753Smm struct stat st; 372228753Smm if (stat(bsdtar->optarg, &st) != 0) 373228753Smm lafe_errc(1, 0, 374228753Smm "Can't open file %s", bsdtar->optarg); 375228753Smm bsdtar->newer_ctime_sec = st.st_ctime; 376228753Smm bsdtar->newer_ctime_nsec = 377228753Smm ARCHIVE_STAT_CTIME_NANOS(&st); 378228753Smm } 379228753Smm break; 380228753Smm case OPTION_NEWER_MTIME: /* GNU tar */ 381228753Smm bsdtar->newer_mtime_sec = get_date(now, bsdtar->optarg); 382228753Smm break; 383228753Smm case OPTION_NEWER_MTIME_THAN: 384228753Smm { 385228753Smm struct stat st; 386228753Smm if (stat(bsdtar->optarg, &st) != 0) 387228753Smm lafe_errc(1, 0, 388228753Smm "Can't open file %s", bsdtar->optarg); 389228753Smm bsdtar->newer_mtime_sec = st.st_mtime; 390228753Smm bsdtar->newer_mtime_nsec = 391228753Smm ARCHIVE_STAT_MTIME_NANOS(&st); 392228753Smm } 393228753Smm break; 394228753Smm case OPTION_NODUMP: /* star */ 395228753Smm bsdtar->option_honor_nodump = 1; 396228753Smm break; 397228753Smm case OPTION_NO_SAME_OWNER: /* GNU tar */ 398228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 399228753Smm break; 400228753Smm case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */ 401228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM; 402228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; 403228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; 404228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; 405228753Smm break; 406228753Smm case OPTION_NULL: /* GNU tar */ 407228753Smm bsdtar->option_null++; 408228753Smm break; 409228753Smm case OPTION_NUMERIC_OWNER: /* GNU tar */ 410228753Smm bsdtar->uname = ""; 411228753Smm bsdtar->gname = ""; 412228753Smm break; 413228753Smm case 'O': /* GNU tar */ 414228753Smm bsdtar->option_stdout = 1; 415228753Smm break; 416228753Smm case 'o': /* SUSv2 and GNU conflict here, but not fatally */ 417228753Smm option_o = 1; /* Record it and resolve it later. */ 418228753Smm break; 419228753Smm case OPTION_ONE_FILE_SYSTEM: /* GNU tar */ 420228753Smm bsdtar->option_dont_traverse_mounts = 1; 421228753Smm break; 422228753Smm case OPTION_OPTIONS: 423228753Smm bsdtar->option_options = bsdtar->optarg; 424228753Smm break; 425228753Smm#if 0 426228753Smm /* 427228753Smm * The common BSD -P option is not necessary, since 428228753Smm * our default is to archive symlinks, not follow 429228753Smm * them. This is convenient, as -P conflicts with GNU 430228753Smm * tar anyway. 431228753Smm */ 432228753Smm case 'P': /* BSD convention */ 433228753Smm /* Default behavior, no option necessary. */ 434228753Smm break; 435228753Smm#endif 436228753Smm case 'P': /* GNU tar */ 437228753Smm bsdtar->extract_flags &= ~SECURITY; 438228753Smm bsdtar->option_absolute_paths = 1; 439228753Smm break; 440228753Smm case 'p': /* GNU tar, star */ 441228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 442228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 443228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 444228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 445228753Smm break; 446228753Smm case OPTION_POSIX: /* GNU tar */ 447228753Smm bsdtar->create_format = "pax"; 448228753Smm break; 449228753Smm case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */ 450228753Smm bsdtar->option_fast_read = 1; 451228753Smm break; 452228753Smm case 'r': /* SUSv2 */ 453228753Smm set_mode(bsdtar, opt); 454228753Smm break; 455228753Smm case 'S': /* NetBSD pax-as-tar */ 456228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE; 457228753Smm break; 458228753Smm case 's': /* NetBSD pax-as-tar */ 459228753Smm#if HAVE_REGEX_H 460228753Smm add_substitution(bsdtar, bsdtar->optarg); 461228753Smm#else 462228753Smm lafe_warnc(0, 463228753Smm "-s is not supported by this version of bsdtar"); 464228753Smm usage(); 465228753Smm#endif 466228753Smm break; 467228753Smm case OPTION_SAME_OWNER: /* GNU tar */ 468228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; 469228753Smm break; 470228753Smm case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ 471228753Smm bsdtar->strip_components = atoi(bsdtar->optarg); 472228753Smm break; 473228753Smm case 'T': /* GNU tar */ 474228753Smm bsdtar->names_from_file = bsdtar->optarg; 475228753Smm break; 476228753Smm case 't': /* SUSv2 */ 477228753Smm set_mode(bsdtar, opt); 478228753Smm bsdtar->verbose++; 479228753Smm break; 480228753Smm case OPTION_TOTALS: /* GNU tar */ 481228753Smm bsdtar->option_totals++; 482228753Smm break; 483228753Smm case 'U': /* GNU tar */ 484228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK; 485228753Smm bsdtar->option_unlink_first = 1; 486228753Smm break; 487228753Smm case 'u': /* SUSv2 */ 488228753Smm set_mode(bsdtar, opt); 489228753Smm break; 490228753Smm case OPTION_UID: /* cpio */ 491228753Smm t = atoi(bsdtar->optarg); 492228753Smm if (t < 0) 493228753Smm lafe_errc(1, 0, 494228753Smm "Argument to --uid must be positive"); 495228753Smm bsdtar->uid = t; 496228753Smm break; 497228753Smm case OPTION_UNAME: /* cpio */ 498228753Smm bsdtar->uname = bsdtar->optarg; 499228753Smm break; 500228753Smm case 'v': /* SUSv2 */ 501228753Smm bsdtar->verbose++; 502228753Smm break; 503228753Smm case OPTION_VERSION: /* GNU convention */ 504228753Smm version(); 505228753Smm break; 506228753Smm#if 0 507228753Smm /* 508228753Smm * The -W longopt feature is handled inside of 509228753Smm * bsdtar_getopt(), so -W is not available here. 510228753Smm */ 511228753Smm case 'W': /* Obscure GNU convention. */ 512228753Smm break; 513228753Smm#endif 514228753Smm case 'w': /* SUSv2 */ 515228753Smm bsdtar->option_interactive = 1; 516228753Smm break; 517228753Smm case 'X': /* GNU tar */ 518228753Smm if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg)) 519228753Smm lafe_errc(1, 0, 520228753Smm "failed to process exclusions from file %s", 521228753Smm bsdtar->optarg); 522228753Smm break; 523228753Smm case 'x': /* SUSv2 */ 524228753Smm set_mode(bsdtar, opt); 525228753Smm break; 526228753Smm case 'y': /* FreeBSD version of GNU tar */ 527228753Smm if (bsdtar->create_compression != '\0') 528228753Smm lafe_errc(1, 0, 529228753Smm "Can't specify both -%c and -%c", opt, 530228753Smm bsdtar->create_compression); 531228753Smm bsdtar->create_compression = opt; 532228753Smm break; 533228753Smm case 'Z': /* GNU tar */ 534228753Smm if (bsdtar->create_compression != '\0') 535228753Smm lafe_errc(1, 0, 536228753Smm "Can't specify both -%c and -%c", opt, 537228753Smm bsdtar->create_compression); 538228753Smm bsdtar->create_compression = opt; 539228753Smm break; 540228753Smm case 'z': /* GNU tar, star, many others */ 541228753Smm if (bsdtar->create_compression != '\0') 542228753Smm lafe_errc(1, 0, 543228753Smm "Can't specify both -%c and -%c", opt, 544228753Smm bsdtar->create_compression); 545228753Smm bsdtar->create_compression = opt; 546228753Smm break; 547228753Smm case OPTION_USE_COMPRESS_PROGRAM: 548228753Smm bsdtar->compress_program = bsdtar->optarg; 549228753Smm break; 550228753Smm default: 551228753Smm usage(); 552228753Smm } 553228753Smm } 554228753Smm 555228753Smm /* 556228753Smm * Sanity-check options. 557228753Smm */ 558228753Smm 559228753Smm /* If no "real" mode was specified, treat -h as --help. */ 560228753Smm if ((bsdtar->mode == '\0') && possible_help_request) { 561228753Smm long_help(); 562228753Smm exit(0); 563228753Smm } 564228753Smm 565228753Smm /* Otherwise, a mode is required. */ 566228753Smm if (bsdtar->mode == '\0') 567228753Smm lafe_errc(1, 0, 568228753Smm "Must specify one of -c, -r, -t, -u, -x"); 569228753Smm 570228753Smm /* Check boolean options only permitted in certain modes. */ 571228753Smm if (bsdtar->option_dont_traverse_mounts) 572228753Smm only_mode(bsdtar, "--one-file-system", "cru"); 573228753Smm if (bsdtar->option_fast_read) 574228753Smm only_mode(bsdtar, "--fast-read", "xt"); 575228753Smm if (bsdtar->option_honor_nodump) 576228753Smm only_mode(bsdtar, "--nodump", "cru"); 577228753Smm if (option_o > 0) { 578228753Smm switch (bsdtar->mode) { 579228753Smm case 'c': 580228753Smm /* 581228753Smm * In GNU tar, -o means "old format." The 582228753Smm * "ustar" format is the closest thing 583228753Smm * supported by libarchive. 584228753Smm */ 585228753Smm bsdtar->create_format = "ustar"; 586228753Smm /* TODO: bsdtar->create_format = "v7"; */ 587228753Smm break; 588228753Smm case 'x': 589228753Smm /* POSIX-compatible behavior. */ 590228753Smm bsdtar->option_no_owner = 1; 591228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 592228753Smm break; 593228753Smm default: 594228753Smm only_mode(bsdtar, "-o", "xc"); 595228753Smm break; 596228753Smm } 597228753Smm } 598228753Smm if (bsdtar->option_no_subdirs) 599228753Smm only_mode(bsdtar, "-n", "cru"); 600228753Smm if (bsdtar->option_stdout) 601228753Smm only_mode(bsdtar, "-O", "xt"); 602228753Smm if (bsdtar->option_unlink_first) 603228753Smm only_mode(bsdtar, "-U", "x"); 604228753Smm if (bsdtar->option_warn_links) 605228753Smm only_mode(bsdtar, "--check-links", "cr"); 606228753Smm 607228753Smm /* Check other parameters only permitted in certain modes. */ 608228753Smm if (bsdtar->create_compression != '\0') { 609228753Smm strcpy(buff, "-?"); 610228753Smm buff[1] = bsdtar->create_compression; 611228753Smm only_mode(bsdtar, buff, "cxt"); 612228753Smm } 613228753Smm if (bsdtar->create_format != NULL) 614228753Smm only_mode(bsdtar, "--format", "cru"); 615228753Smm if (bsdtar->symlink_mode != '\0') { 616228753Smm strcpy(buff, "-?"); 617228753Smm buff[1] = bsdtar->symlink_mode; 618228753Smm only_mode(bsdtar, buff, "cru"); 619228753Smm } 620228753Smm if (bsdtar->strip_components != 0) 621228753Smm only_mode(bsdtar, "--strip-components", "xt"); 622228753Smm 623228753Smm switch(bsdtar->mode) { 624228753Smm case 'c': 625228753Smm tar_mode_c(bsdtar); 626228753Smm break; 627228753Smm case 'r': 628228753Smm tar_mode_r(bsdtar); 629228753Smm break; 630228753Smm case 't': 631228753Smm tar_mode_t(bsdtar); 632228753Smm break; 633228753Smm case 'u': 634228753Smm tar_mode_u(bsdtar); 635228753Smm break; 636228753Smm case 'x': 637228753Smm tar_mode_x(bsdtar); 638228753Smm break; 639228753Smm } 640228753Smm 641228753Smm lafe_cleanup_exclusions(&bsdtar->matching); 642228753Smm#if HAVE_REGEX_H 643228753Smm cleanup_substitution(bsdtar); 644228753Smm#endif 645228753Smm 646228753Smm if (bsdtar->return_value != 0) 647228753Smm lafe_warnc(0, 648228753Smm "Error exit delayed from previous errors."); 649228753Smm return (bsdtar->return_value); 650228753Smm} 651228753Smm 652228753Smmstatic void 653228753Smmset_mode(struct bsdtar *bsdtar, char opt) 654228753Smm{ 655228753Smm if (bsdtar->mode != '\0' && bsdtar->mode != opt) 656228753Smm lafe_errc(1, 0, 657228753Smm "Can't specify both -%c and -%c", opt, bsdtar->mode); 658228753Smm bsdtar->mode = opt; 659228753Smm} 660228753Smm 661228753Smm/* 662228753Smm * Verify that the mode is correct. 663228753Smm */ 664228753Smmstatic void 665228753Smmonly_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) 666228753Smm{ 667228753Smm if (strchr(valid_modes, bsdtar->mode) == NULL) 668228753Smm lafe_errc(1, 0, 669228753Smm "Option %s is not permitted in mode -%c", 670228753Smm opt, bsdtar->mode); 671228753Smm} 672228753Smm 673228753Smm 674228753Smmvoid 675228753Smmusage(void) 676228753Smm{ 677228753Smm const char *p; 678228753Smm 679228753Smm p = lafe_progname; 680228753Smm 681228753Smm fprintf(stderr, "Usage:\n"); 682228753Smm fprintf(stderr, " List: %s -tf <archive-filename>\n", p); 683228753Smm fprintf(stderr, " Extract: %s -xf <archive-filename>\n", p); 684228753Smm fprintf(stderr, " Create: %s -cf <archive-filename> [filenames...]\n", p); 685228753Smm fprintf(stderr, " Help: %s --help\n", p); 686228753Smm exit(1); 687228753Smm} 688228753Smm 689228753Smmstatic void 690228753Smmversion(void) 691228753Smm{ 692228753Smm printf("bsdtar %s - %s\n", 693228753Smm BSDTAR_VERSION_STRING, 694228753Smm archive_version()); 695228753Smm exit(0); 696228753Smm} 697228753Smm 698228753Smmstatic const char *long_help_msg = 699228753Smm "First option must be a mode specifier:\n" 700228753Smm " -c Create -r Add/Replace -t List -u Update -x Extract\n" 701228753Smm "Common Options:\n" 702228753Smm " -b # Use # 512-byte records per I/O block\n" 703228753Smm " -f <filename> Location of archive (default " _PATH_DEFTAPE ")\n" 704228753Smm " -v Verbose\n" 705228753Smm " -w Interactive\n" 706228753Smm "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n" 707228753Smm " <file>, <dir> add these items to archive\n" 708228753Smm " -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n" 709228753Smm " --format {ustar|pax|cpio|shar} Select archive format\n" 710228753Smm " --exclude <pattern> Skip files that match pattern\n" 711228753Smm " -C <dir> Change to <dir> before processing remaining files\n" 712228753Smm " @<archive> Add entries from <archive> to output\n" 713228753Smm "List: %p -t [options] [<patterns>]\n" 714228753Smm " <patterns> If specified, list only entries that match\n" 715228753Smm "Extract: %p -x [options] [<patterns>]\n" 716228753Smm " <patterns> If specified, extract only entries that match\n" 717228753Smm " -k Keep (don't overwrite) existing files\n" 718228753Smm " -m Don't restore modification times\n" 719228753Smm " -O Write entries to stdout, don't restore to disk\n" 720228753Smm " -p Restore permissions (including ACLs, owner, file flags)\n"; 721228753Smm 722228753Smm 723228753Smm/* 724228753Smm * Note that the word 'bsdtar' will always appear in the first line 725228753Smm * of output. 726228753Smm * 727228753Smm * In particular, /bin/sh scripts that need to test for the presence 728228753Smm * of bsdtar can use the following template: 729228753Smm * 730228753Smm * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \ 731228753Smm * echo bsdtar; else echo not bsdtar; fi 732228753Smm */ 733228753Smmstatic void 734228753Smmlong_help(void) 735228753Smm{ 736228753Smm const char *prog; 737228753Smm const char *p; 738228753Smm 739228753Smm prog = lafe_progname; 740228753Smm 741228753Smm fflush(stderr); 742228753Smm 743228753Smm p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : ""; 744228753Smm printf("%s%s: manipulate archive files\n", prog, p); 745228753Smm 746228753Smm for (p = long_help_msg; *p != '\0'; p++) { 747228753Smm if (*p == '%') { 748228753Smm if (p[1] == 'p') { 749228753Smm fputs(prog, stdout); 750228753Smm p++; 751228753Smm } else 752228753Smm putchar('%'); 753228753Smm } else 754228753Smm putchar(*p); 755228753Smm } 756228753Smm version(); 757228753Smm} 758