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" 27228763Smm__FBSDID("$FreeBSD$"); 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 35232153Smm#ifdef HAVE_COPYFILE_H 36232153Smm#include <copyfile.h> 37232153Smm#endif 38228753Smm#ifdef HAVE_ERRNO_H 39228753Smm#include <errno.h> 40228753Smm#endif 41228753Smm#ifdef HAVE_FCNTL_H 42228753Smm#include <fcntl.h> 43228753Smm#endif 44228753Smm#ifdef HAVE_LANGINFO_H 45228753Smm#include <langinfo.h> 46228753Smm#endif 47228753Smm#ifdef HAVE_LOCALE_H 48228753Smm#include <locale.h> 49228753Smm#endif 50228753Smm#ifdef HAVE_PATHS_H 51228753Smm#include <paths.h> 52228753Smm#endif 53228753Smm#ifdef HAVE_SIGNAL_H 54228753Smm#include <signal.h> 55228753Smm#endif 56228753Smm#include <stdio.h> 57228753Smm#ifdef HAVE_STDLIB_H 58228753Smm#include <stdlib.h> 59228753Smm#endif 60228753Smm#ifdef HAVE_STRING_H 61228753Smm#include <string.h> 62228753Smm#endif 63228753Smm#ifdef HAVE_TIME_H 64228753Smm#include <time.h> 65228753Smm#endif 66228753Smm#ifdef HAVE_UNISTD_H 67228753Smm#include <unistd.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 83232153Smm#if defined(__APPLE__) 84232153Smm#undef _PATH_DEFTAPE 85232153Smm#define _PATH_DEFTAPE "-" /* Mac OS has no tape support, default to stdio. */ 86232153Smm#endif 87228753Smm 88228753Smm#ifndef _PATH_DEFTAPE 89228753Smm#define _PATH_DEFTAPE "/dev/tape" 90228753Smm#endif 91228753Smm 92228753Smm#ifdef __MINGW32__ 93228753Smmint _CRT_glob = 0; /* Disable broken CRT globbing. */ 94228753Smm#endif 95228753Smm 96228753Smm#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) 97228753Smmstatic volatile int siginfo_occurred; 98228753Smm 99228753Smmstatic void 100228753Smmsiginfo_handler(int sig) 101228753Smm{ 102228753Smm (void)sig; /* UNUSED */ 103228753Smm siginfo_occurred = 1; 104228753Smm} 105228753Smm 106228753Smmint 107228753Smmneed_report(void) 108228753Smm{ 109228753Smm int r = siginfo_occurred; 110228753Smm siginfo_occurred = 0; 111228753Smm return (r); 112228753Smm} 113228753Smm#else 114228753Smmint 115228753Smmneed_report(void) 116228753Smm{ 117228753Smm return (0); 118228753Smm} 119228753Smm#endif 120228753Smm 121228753Smmstatic void long_help(void); 122228753Smmstatic void only_mode(struct bsdtar *, const char *opt, 123228753Smm const char *valid); 124228753Smmstatic void set_mode(struct bsdtar *, char opt); 125228753Smmstatic void version(void); 126228753Smm 127228753Smm/* A basic set of security flags to request from libarchive. */ 128228753Smm#define SECURITY \ 129228753Smm (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ 130228753Smm | ARCHIVE_EXTRACT_SECURE_NODOTDOT) 131228753Smm 132228753Smmint 133228753Smmmain(int argc, char **argv) 134228753Smm{ 135228753Smm struct bsdtar *bsdtar, bsdtar_storage; 136228753Smm int opt, t; 137248616Smm char compression, compression2; 138248616Smm const char *compression_name, *compression2_name; 139248616Smm const char *compress_program; 140248616Smm char option_a, option_o; 141228753Smm char possible_help_request; 142228753Smm char buff[16]; 143228753Smm 144228753Smm /* 145228753Smm * Use a pointer for consistency, but stack-allocated storage 146228753Smm * for ease of cleanup. 147228753Smm */ 148232153Smm bsdtar = &bsdtar_storage; 149228753Smm memset(bsdtar, 0, sizeof(*bsdtar)); 150228753Smm bsdtar->fd = -1; /* Mark as "unused" */ 151228753Smm bsdtar->gid = -1; 152228753Smm bsdtar->uid = -1; 153248616Smm option_a = option_o = 0; 154248616Smm compression = compression2 = '\0'; 155248616Smm compression_name = compression2_name = NULL; 156248616Smm compress_program = NULL; 157228753Smm 158232153Smm#if defined(HAVE_SIGACTION) 159232153Smm { /* Set up signal handling. */ 160228753Smm struct sigaction sa; 161228753Smm sa.sa_handler = siginfo_handler; 162228753Smm sigemptyset(&sa.sa_mask); 163228753Smm sa.sa_flags = 0; 164228753Smm#ifdef SIGINFO 165228753Smm if (sigaction(SIGINFO, &sa, NULL)) 166228753Smm lafe_errc(1, errno, "sigaction(SIGINFO) failed"); 167228753Smm#endif 168228753Smm#ifdef SIGUSR1 169228753Smm /* ... and treat SIGUSR1 the same way as SIGINFO. */ 170228753Smm if (sigaction(SIGUSR1, &sa, NULL)) 171228753Smm lafe_errc(1, errno, "sigaction(SIGUSR1) failed"); 172228753Smm#endif 173232153Smm#ifdef SIGPIPE 174232153Smm /* Ignore SIGPIPE signals. */ 175232153Smm sa.sa_handler = SIG_IGN; 176232153Smm sigaction(SIGPIPE, &sa, NULL); 177232153Smm#endif 178228753Smm } 179228753Smm#endif 180228753Smm 181228753Smm 182228753Smm /* Need lafe_progname before calling lafe_warnc. */ 183228753Smm if (*argv == NULL) 184228753Smm lafe_progname = "bsdtar"; 185228753Smm else { 186228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 187228753Smm lafe_progname = strrchr(*argv, '\\'); 188232153Smm if (strrchr(*argv, '/') > lafe_progname) 189232153Smm#endif 190228753Smm lafe_progname = strrchr(*argv, '/'); 191228753Smm if (lafe_progname != NULL) 192228753Smm lafe_progname++; 193228753Smm else 194228753Smm lafe_progname = *argv; 195228753Smm } 196228753Smm 197228753Smm#if HAVE_SETLOCALE 198228753Smm if (setlocale(LC_ALL, "") == NULL) 199228753Smm lafe_warnc(0, "Failed to set default locale"); 200228753Smm#endif 201228753Smm#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) 202228753Smm bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 203228753Smm#endif 204228753Smm possible_help_request = 0; 205228753Smm 206228753Smm /* Look up uid of current user for future reference */ 207228753Smm bsdtar->user_uid = geteuid(); 208228753Smm 209228753Smm /* Default: open tape drive. */ 210228753Smm bsdtar->filename = getenv("TAPE"); 211228753Smm if (bsdtar->filename == NULL) 212228753Smm bsdtar->filename = _PATH_DEFTAPE; 213228753Smm 214232153Smm /* Default block size settings. */ 215232153Smm bsdtar->bytes_per_block = DEFAULT_BYTES_PER_BLOCK; 216232153Smm /* Allow library to default this unless user specifies -b. */ 217232153Smm bsdtar->bytes_in_last_block = -1; 218232153Smm 219228753Smm /* Default: preserve mod time on extract */ 220228753Smm bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME; 221228753Smm 222228753Smm /* Default: Perform basic security checks. */ 223228753Smm bsdtar->extract_flags |= SECURITY; 224228753Smm 225228753Smm#ifndef _WIN32 226228753Smm /* On POSIX systems, assume --same-owner and -p when run by 227228753Smm * the root user. This doesn't make any sense on Windows. */ 228228753Smm if (bsdtar->user_uid == 0) { 229228753Smm /* --same-owner */ 230228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; 231228753Smm /* -p */ 232228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 233228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 234228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 235228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 236232153Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; 237228753Smm } 238228753Smm#endif 239228753Smm 240232153Smm /* 241232153Smm * Enable Mac OS "copyfile()" extension by default. 242232153Smm * This has no effect on other platforms. 243232153Smm */ 244238856Smm bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE; 245232153Smm#ifdef COPYFILE_DISABLE_VAR 246232153Smm if (getenv(COPYFILE_DISABLE_VAR)) 247238856Smm bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; 248232153Smm#endif 249238856Smm bsdtar->matching = archive_match_new(); 250238856Smm if (bsdtar->matching == NULL) 251238856Smm lafe_errc(1, errno, "Out of memory"); 252248616Smm bsdtar->cset = cset_new(); 253248616Smm if (bsdtar->cset == NULL) 254248616Smm lafe_errc(1, errno, "Out of memory"); 255232153Smm 256228753Smm bsdtar->argv = argv; 257228753Smm bsdtar->argc = argc; 258228753Smm 259228753Smm /* 260228753Smm * Comments following each option indicate where that option 261228753Smm * originated: SUSv2, POSIX, GNU tar, star, etc. If there's 262228753Smm * no such comment, then I don't know of anyone else who 263228753Smm * implements that option. 264228753Smm */ 265228753Smm while ((opt = bsdtar_getopt(bsdtar)) != -1) { 266228753Smm switch (opt) { 267248616Smm case 'a': /* GNU tar */ 268248616Smm option_a = 1; /* Record it and resolve it later. */ 269248616Smm break; 270228753Smm case 'B': /* GNU tar */ 271228753Smm /* libarchive doesn't need this; just ignore it. */ 272228753Smm break; 273228753Smm case 'b': /* SUSv2 */ 274232153Smm t = atoi(bsdtar->argument); 275228753Smm if (t <= 0 || t > 8192) 276228753Smm lafe_errc(1, 0, 277228753Smm "Argument to -b is out of range (1..8192)"); 278228753Smm bsdtar->bytes_per_block = 512 * t; 279232153Smm /* Explicit -b forces last block size. */ 280232153Smm bsdtar->bytes_in_last_block = bsdtar->bytes_per_block; 281228753Smm break; 282248616Smm case OPTION_B64ENCODE: 283248616Smm if (compression2 != '\0') 284248616Smm lafe_errc(1, 0, 285248616Smm "Can't specify both --uuencode and " 286248616Smm "--b64encode"); 287248616Smm compression2 = opt; 288248616Smm compression2_name = "b64encode"; 289248616Smm break; 290228753Smm case 'C': /* GNU tar */ 291232153Smm if (strlen(bsdtar->argument) == 0) 292232153Smm lafe_errc(1, 0, 293232153Smm "Meaningless option: -C ''"); 294232153Smm 295232153Smm set_chdir(bsdtar, bsdtar->argument); 296228753Smm break; 297228753Smm case 'c': /* SUSv2 */ 298228753Smm set_mode(bsdtar, opt); 299228753Smm break; 300228753Smm case OPTION_CHECK_LINKS: /* GNU tar */ 301228753Smm bsdtar->option_warn_links = 1; 302228753Smm break; 303228753Smm case OPTION_CHROOT: /* NetBSD */ 304228753Smm bsdtar->option_chroot = 1; 305228753Smm break; 306232153Smm case OPTION_DISABLE_COPYFILE: /* Mac OS X */ 307238856Smm bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE; 308232153Smm break; 309228753Smm case OPTION_EXCLUDE: /* GNU tar */ 310238856Smm if (archive_match_exclude_pattern( 311238856Smm bsdtar->matching, bsdtar->argument) != ARCHIVE_OK) 312228753Smm lafe_errc(1, 0, 313232153Smm "Couldn't exclude %s\n", bsdtar->argument); 314228753Smm break; 315228753Smm case OPTION_FORMAT: /* GNU tar, others */ 316248616Smm cset_set_format(bsdtar->cset, bsdtar->argument); 317228753Smm break; 318228753Smm case 'f': /* SUSv2 */ 319232153Smm bsdtar->filename = bsdtar->argument; 320228753Smm break; 321228753Smm case OPTION_GID: /* cpio */ 322232153Smm t = atoi(bsdtar->argument); 323228753Smm if (t < 0) 324228753Smm lafe_errc(1, 0, 325228753Smm "Argument to --gid must be positive"); 326228753Smm bsdtar->gid = t; 327228753Smm break; 328228753Smm case OPTION_GNAME: /* cpio */ 329232153Smm bsdtar->gname = bsdtar->argument; 330228753Smm break; 331248616Smm case OPTION_GRZIP: 332248616Smm if (compression != '\0') 333248616Smm lafe_errc(1, 0, 334248616Smm "Can't specify both -%c and -%c", opt, 335248616Smm compression); 336248616Smm compression = opt; 337248616Smm compression_name = "grzip"; 338248616Smm break; 339228753Smm case 'H': /* BSD convention */ 340228753Smm bsdtar->symlink_mode = 'H'; 341228753Smm break; 342228753Smm case 'h': /* Linux Standards Base, gtar; synonym for -L */ 343228753Smm bsdtar->symlink_mode = 'L'; 344228753Smm /* Hack: -h by itself is the "help" command. */ 345228753Smm possible_help_request = 1; 346228753Smm break; 347228753Smm case OPTION_HELP: /* GNU tar, others */ 348228753Smm long_help(); 349228753Smm exit(0); 350228753Smm break; 351248616Smm case OPTION_HFS_COMPRESSION: /* Mac OS X v10.6 or later */ 352248616Smm bsdtar->extract_flags |= 353248616Smm ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED; 354248616Smm break; 355228753Smm case 'I': /* GNU tar */ 356228753Smm /* 357228753Smm * TODO: Allow 'names' to come from an archive, 358228753Smm * not just a text file. Design a good UI for 359228753Smm * allowing names and mode/owner to be read 360228753Smm * from an archive, with contents coming from 361228753Smm * disk. This can be used to "refresh" an 362228753Smm * archive or to design archives with special 363228753Smm * permissions without having to create those 364228753Smm * permissions on disk. 365228753Smm */ 366232153Smm bsdtar->names_from_file = bsdtar->argument; 367228753Smm break; 368228753Smm case OPTION_INCLUDE: 369228753Smm /* 370232153Smm * No one else has the @archive extension, so 371232153Smm * no one else needs this to filter entries 372228753Smm * when transforming archives. 373228753Smm */ 374238856Smm if (archive_match_include_pattern(bsdtar->matching, 375238856Smm bsdtar->argument) != ARCHIVE_OK) 376228753Smm lafe_errc(1, 0, 377228753Smm "Failed to add %s to inclusion list", 378232153Smm bsdtar->argument); 379228753Smm break; 380228753Smm case 'j': /* GNU tar */ 381248616Smm if (compression != '\0') 382228753Smm lafe_errc(1, 0, 383228753Smm "Can't specify both -%c and -%c", opt, 384248616Smm compression); 385248616Smm compression = opt; 386248616Smm compression_name = "bzip2"; 387228753Smm break; 388228753Smm case 'J': /* GNU tar 1.21 and later */ 389248616Smm if (compression != '\0') 390228753Smm lafe_errc(1, 0, 391228753Smm "Can't specify both -%c and -%c", opt, 392248616Smm compression); 393248616Smm compression = opt; 394248616Smm compression_name = "xz"; 395228753Smm break; 396228753Smm case 'k': /* GNU tar */ 397228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; 398228753Smm break; 399228753Smm case OPTION_KEEP_NEWER_FILES: /* GNU tar */ 400228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; 401228753Smm break; 402228753Smm case 'L': /* BSD convention */ 403228753Smm bsdtar->symlink_mode = 'L'; 404228753Smm break; 405228753Smm case 'l': /* SUSv2 and GNU tar beginning with 1.16 */ 406228753Smm /* GNU tar 1.13 used -l for --one-file-system */ 407228753Smm bsdtar->option_warn_links = 1; 408228753Smm break; 409248616Smm case OPTION_LRZIP: 410232153Smm case OPTION_LZIP: /* GNU tar beginning with 1.23 */ 411232153Smm case OPTION_LZMA: /* GNU tar beginning with 1.20 */ 412248616Smm case OPTION_LZOP: /* GNU tar beginning with 1.21 */ 413248616Smm if (compression != '\0') 414228753Smm lafe_errc(1, 0, 415228753Smm "Can't specify both -%c and -%c", opt, 416248616Smm compression); 417248616Smm compression = opt; 418248616Smm switch (opt) { 419248616Smm case OPTION_LRZIP: compression_name = "lrzip"; break; 420248616Smm case OPTION_LZIP: compression_name = "lzip"; break; 421248616Smm case OPTION_LZMA: compression_name = "lzma"; break; 422248616Smm case OPTION_LZOP: compression_name = "lzop"; break; 423248616Smm } 424228753Smm break; 425228753Smm case 'm': /* SUSv2 */ 426228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; 427228753Smm break; 428228753Smm case 'n': /* GNU tar */ 429228753Smm bsdtar->option_no_subdirs = 1; 430228753Smm break; 431228753Smm /* 432228753Smm * Selecting files by time: 433228753Smm * --newer-?time='date' Only files newer than 'date' 434228753Smm * --newer-?time-than='file' Only files newer than time 435228753Smm * on specified file (useful for incremental backups) 436228753Smm */ 437228753Smm case OPTION_NEWER_CTIME: /* GNU tar */ 438238856Smm if (archive_match_include_date(bsdtar->matching, 439238856Smm ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER, 440238856Smm bsdtar->argument) != ARCHIVE_OK) 441238856Smm lafe_errc(1, 0, "Error : %s", 442238856Smm archive_error_string(bsdtar->matching)); 443228753Smm break; 444228753Smm case OPTION_NEWER_CTIME_THAN: 445238856Smm if (archive_match_include_file_time(bsdtar->matching, 446238856Smm ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER, 447238856Smm bsdtar->argument) != ARCHIVE_OK) 448238856Smm lafe_errc(1, 0, "Error : %s", 449238856Smm archive_error_string(bsdtar->matching)); 450228753Smm break; 451228753Smm case OPTION_NEWER_MTIME: /* GNU tar */ 452238856Smm if (archive_match_include_date(bsdtar->matching, 453238856Smm ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER, 454238856Smm bsdtar->argument) != ARCHIVE_OK) 455238856Smm lafe_errc(1, 0, "Error : %s", 456238856Smm archive_error_string(bsdtar->matching)); 457228753Smm break; 458228753Smm case OPTION_NEWER_MTIME_THAN: 459238856Smm if (archive_match_include_file_time(bsdtar->matching, 460238856Smm ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER, 461238856Smm bsdtar->argument) != ARCHIVE_OK) 462238856Smm lafe_errc(1, 0, "Error : %s", 463238856Smm archive_error_string(bsdtar->matching)); 464228753Smm break; 465228753Smm case OPTION_NODUMP: /* star */ 466238856Smm bsdtar->readdisk_flags |= ARCHIVE_READDISK_HONOR_NODUMP; 467228753Smm break; 468248616Smm case OPTION_NOPRESERVE_HFS_COMPRESSION: 469248616Smm /* Mac OS X v10.6 or later */ 470248616Smm bsdtar->extract_flags |= 471248616Smm ARCHIVE_EXTRACT_NO_HFS_COMPRESSION; 472248616Smm break; 473228753Smm case OPTION_NO_SAME_OWNER: /* GNU tar */ 474228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 475228753Smm break; 476228753Smm case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */ 477228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM; 478228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL; 479228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR; 480228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS; 481232153Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA; 482228753Smm break; 483228753Smm case OPTION_NULL: /* GNU tar */ 484228753Smm bsdtar->option_null++; 485228753Smm break; 486228753Smm case OPTION_NUMERIC_OWNER: /* GNU tar */ 487228753Smm bsdtar->uname = ""; 488228753Smm bsdtar->gname = ""; 489232153Smm bsdtar->option_numeric_owner++; 490228753Smm break; 491228753Smm case 'O': /* GNU tar */ 492228753Smm bsdtar->option_stdout = 1; 493228753Smm break; 494228753Smm case 'o': /* SUSv2 and GNU conflict here, but not fatally */ 495228753Smm option_o = 1; /* Record it and resolve it later. */ 496228753Smm break; 497248616Smm /* 498248616Smm * Selecting files by time: 499248616Smm * --older-?time='date' Only files older than 'date' 500248616Smm * --older-?time-than='file' Only files older than time 501248616Smm * on specified file 502248616Smm */ 503248616Smm case OPTION_OLDER_CTIME: 504248616Smm if (archive_match_include_date(bsdtar->matching, 505248616Smm ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER, 506248616Smm bsdtar->argument) != ARCHIVE_OK) 507248616Smm lafe_errc(1, 0, "Error : %s", 508248616Smm archive_error_string(bsdtar->matching)); 509248616Smm break; 510248616Smm case OPTION_OLDER_CTIME_THAN: 511248616Smm if (archive_match_include_file_time(bsdtar->matching, 512248616Smm ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER, 513248616Smm bsdtar->argument) != ARCHIVE_OK) 514248616Smm lafe_errc(1, 0, "Error : %s", 515248616Smm archive_error_string(bsdtar->matching)); 516248616Smm break; 517248616Smm case OPTION_OLDER_MTIME: 518248616Smm if (archive_match_include_date(bsdtar->matching, 519248616Smm ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER, 520248616Smm bsdtar->argument) != ARCHIVE_OK) 521248616Smm lafe_errc(1, 0, "Error : %s", 522248616Smm archive_error_string(bsdtar->matching)); 523248616Smm break; 524248616Smm case OPTION_OLDER_MTIME_THAN: 525248616Smm if (archive_match_include_file_time(bsdtar->matching, 526248616Smm ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER, 527248616Smm bsdtar->argument) != ARCHIVE_OK) 528248616Smm lafe_errc(1, 0, "Error : %s", 529248616Smm archive_error_string(bsdtar->matching)); 530248616Smm break; 531228753Smm case OPTION_ONE_FILE_SYSTEM: /* GNU tar */ 532238856Smm bsdtar->readdisk_flags |= 533238856Smm ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS; 534228753Smm break; 535228753Smm case OPTION_OPTIONS: 536232153Smm bsdtar->option_options = bsdtar->argument; 537228753Smm break; 538228753Smm#if 0 539228753Smm /* 540228753Smm * The common BSD -P option is not necessary, since 541228753Smm * our default is to archive symlinks, not follow 542228753Smm * them. This is convenient, as -P conflicts with GNU 543228753Smm * tar anyway. 544228753Smm */ 545228753Smm case 'P': /* BSD convention */ 546228753Smm /* Default behavior, no option necessary. */ 547228753Smm break; 548228753Smm#endif 549228753Smm case 'P': /* GNU tar */ 550228753Smm bsdtar->extract_flags &= ~SECURITY; 551228753Smm bsdtar->option_absolute_paths = 1; 552228753Smm break; 553228753Smm case 'p': /* GNU tar, star */ 554228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM; 555228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL; 556228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; 557228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; 558232153Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; 559228753Smm break; 560228753Smm case OPTION_POSIX: /* GNU tar */ 561248616Smm cset_set_format(bsdtar->cset, "pax"); 562228753Smm break; 563228753Smm case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */ 564228753Smm bsdtar->option_fast_read = 1; 565228753Smm break; 566228753Smm case 'r': /* SUSv2 */ 567228753Smm set_mode(bsdtar, opt); 568228753Smm break; 569228753Smm case 'S': /* NetBSD pax-as-tar */ 570228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE; 571228753Smm break; 572228753Smm case 's': /* NetBSD pax-as-tar */ 573248616Smm#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) 574232153Smm add_substitution(bsdtar, bsdtar->argument); 575228753Smm#else 576228753Smm lafe_warnc(0, 577228753Smm "-s is not supported by this version of bsdtar"); 578228753Smm usage(); 579228753Smm#endif 580228753Smm break; 581228753Smm case OPTION_SAME_OWNER: /* GNU tar */ 582228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; 583228753Smm break; 584228753Smm case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ 585232153Smm errno = 0; 586232153Smm bsdtar->strip_components = strtol(bsdtar->argument, 587232153Smm NULL, 0); 588232153Smm if (errno) 589232153Smm lafe_errc(1, 0, 590232153Smm "Invalid --strip-components argument: %s", 591232153Smm bsdtar->argument); 592228753Smm break; 593228753Smm case 'T': /* GNU tar */ 594232153Smm bsdtar->names_from_file = bsdtar->argument; 595228753Smm break; 596228753Smm case 't': /* SUSv2 */ 597228753Smm set_mode(bsdtar, opt); 598228753Smm bsdtar->verbose++; 599228753Smm break; 600228753Smm case OPTION_TOTALS: /* GNU tar */ 601228753Smm bsdtar->option_totals++; 602228753Smm break; 603228753Smm case 'U': /* GNU tar */ 604228753Smm bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK; 605228753Smm bsdtar->option_unlink_first = 1; 606228753Smm break; 607228753Smm case 'u': /* SUSv2 */ 608228753Smm set_mode(bsdtar, opt); 609228753Smm break; 610228753Smm case OPTION_UID: /* cpio */ 611232153Smm t = atoi(bsdtar->argument); 612228753Smm if (t < 0) 613228753Smm lafe_errc(1, 0, 614228753Smm "Argument to --uid must be positive"); 615228753Smm bsdtar->uid = t; 616228753Smm break; 617228753Smm case OPTION_UNAME: /* cpio */ 618232153Smm bsdtar->uname = bsdtar->argument; 619228753Smm break; 620248616Smm case OPTION_UUENCODE: 621248616Smm if (compression2 != '\0') 622248616Smm lafe_errc(1, 0, 623248616Smm "Can't specify both --uuencode and " 624248616Smm "--b64encode"); 625248616Smm compression2 = opt; 626248616Smm compression2_name = "uuencode"; 627248616Smm break; 628228753Smm case 'v': /* SUSv2 */ 629228753Smm bsdtar->verbose++; 630228753Smm break; 631228753Smm case OPTION_VERSION: /* GNU convention */ 632228753Smm version(); 633228753Smm break; 634228753Smm#if 0 635228753Smm /* 636228753Smm * The -W longopt feature is handled inside of 637228753Smm * bsdtar_getopt(), so -W is not available here. 638228753Smm */ 639228753Smm case 'W': /* Obscure GNU convention. */ 640228753Smm break; 641228753Smm#endif 642228753Smm case 'w': /* SUSv2 */ 643228753Smm bsdtar->option_interactive = 1; 644228753Smm break; 645228753Smm case 'X': /* GNU tar */ 646238856Smm if (archive_match_exclude_pattern_from_file( 647238856Smm bsdtar->matching, bsdtar->argument, 0) 648238856Smm != ARCHIVE_OK) 649238856Smm lafe_errc(1, 0, "Error : %s", 650238856Smm archive_error_string(bsdtar->matching)); 651228753Smm break; 652228753Smm case 'x': /* SUSv2 */ 653228753Smm set_mode(bsdtar, opt); 654228753Smm break; 655228753Smm case 'y': /* FreeBSD version of GNU tar */ 656248616Smm if (compression != '\0') 657228753Smm lafe_errc(1, 0, 658228753Smm "Can't specify both -%c and -%c", opt, 659248616Smm compression); 660248616Smm compression = opt; 661248616Smm compression_name = "bzip2"; 662228753Smm break; 663228753Smm case 'Z': /* GNU tar */ 664248616Smm if (compression != '\0') 665228753Smm lafe_errc(1, 0, 666228753Smm "Can't specify both -%c and -%c", opt, 667248616Smm compression); 668248616Smm compression = opt; 669248616Smm compression_name = "compress"; 670228753Smm break; 671228753Smm case 'z': /* GNU tar, star, many others */ 672248616Smm if (compression != '\0') 673228753Smm lafe_errc(1, 0, 674228753Smm "Can't specify both -%c and -%c", opt, 675248616Smm compression); 676248616Smm compression = opt; 677248616Smm compression_name = "gzip"; 678228753Smm break; 679228753Smm case OPTION_USE_COMPRESS_PROGRAM: 680248616Smm compress_program = bsdtar->argument; 681228753Smm break; 682228753Smm default: 683228753Smm usage(); 684228753Smm } 685228753Smm } 686228753Smm 687228753Smm /* 688228753Smm * Sanity-check options. 689228753Smm */ 690228753Smm 691228753Smm /* If no "real" mode was specified, treat -h as --help. */ 692228753Smm if ((bsdtar->mode == '\0') && possible_help_request) { 693228753Smm long_help(); 694228753Smm exit(0); 695228753Smm } 696228753Smm 697228753Smm /* Otherwise, a mode is required. */ 698228753Smm if (bsdtar->mode == '\0') 699228753Smm lafe_errc(1, 0, 700228753Smm "Must specify one of -c, -r, -t, -u, -x"); 701228753Smm 702228753Smm /* Check boolean options only permitted in certain modes. */ 703248616Smm if (option_a) 704248616Smm only_mode(bsdtar, "-a", "c"); 705238856Smm if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) 706228753Smm only_mode(bsdtar, "--one-file-system", "cru"); 707228753Smm if (bsdtar->option_fast_read) 708228753Smm only_mode(bsdtar, "--fast-read", "xt"); 709248616Smm if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) 710248616Smm only_mode(bsdtar, "--hfsCompression", "x"); 711248616Smm if (bsdtar->extract_flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) 712248616Smm only_mode(bsdtar, "--nopreserveHFSCompression", "x"); 713238856Smm if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP) 714228753Smm only_mode(bsdtar, "--nodump", "cru"); 715228753Smm if (option_o > 0) { 716228753Smm switch (bsdtar->mode) { 717228753Smm case 'c': 718228753Smm /* 719228753Smm * In GNU tar, -o means "old format." The 720228753Smm * "ustar" format is the closest thing 721228753Smm * supported by libarchive. 722228753Smm */ 723248616Smm cset_set_format(bsdtar->cset, "ustar"); 724228753Smm /* TODO: bsdtar->create_format = "v7"; */ 725228753Smm break; 726228753Smm case 'x': 727228753Smm /* POSIX-compatible behavior. */ 728228753Smm bsdtar->option_no_owner = 1; 729228753Smm bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; 730228753Smm break; 731228753Smm default: 732228753Smm only_mode(bsdtar, "-o", "xc"); 733228753Smm break; 734228753Smm } 735228753Smm } 736228753Smm if (bsdtar->option_no_subdirs) 737228753Smm only_mode(bsdtar, "-n", "cru"); 738228753Smm if (bsdtar->option_stdout) 739228753Smm only_mode(bsdtar, "-O", "xt"); 740228753Smm if (bsdtar->option_unlink_first) 741228753Smm only_mode(bsdtar, "-U", "x"); 742228753Smm if (bsdtar->option_warn_links) 743228753Smm only_mode(bsdtar, "--check-links", "cr"); 744228753Smm 745248616Smm if (option_a && cset_auto_compress(bsdtar->cset, bsdtar->filename)) { 746248616Smm /* Ignore specified compressions if auto-compress works. */ 747248616Smm compression = '\0'; 748248616Smm compression2 = '\0'; 749248616Smm } 750228753Smm /* Check other parameters only permitted in certain modes. */ 751248616Smm if (compress_program != NULL) { 752248616Smm only_mode(bsdtar, "--use-compress-program", "cxt"); 753248616Smm cset_add_filter_program(bsdtar->cset, compress_program); 754248616Smm /* Ignore specified compressions. */ 755248616Smm compression = '\0'; 756248616Smm compression2 = '\0'; 757248616Smm } 758248616Smm if (compression != '\0') { 759248616Smm switch (compression) { 760248616Smm case 'J': case 'j': case 'y': case 'Z': case 'z': 761248616Smm strcpy(buff, "-?"); 762248616Smm buff[1] = compression; 763248616Smm break; 764248616Smm default: 765248616Smm strcpy(buff, "--"); 766248616Smm strcat(buff, compression_name); 767248616Smm break; 768248616Smm } 769228753Smm only_mode(bsdtar, buff, "cxt"); 770248616Smm cset_add_filter(bsdtar->cset, compression_name); 771228753Smm } 772248616Smm if (compression2 != '\0') { 773248616Smm strcpy(buff, "--"); 774248616Smm strcat(buff, compression2_name); 775248616Smm only_mode(bsdtar, buff, "cxt"); 776248616Smm cset_add_filter(bsdtar->cset, compression2_name); 777248616Smm } 778248616Smm if (cset_get_format(bsdtar->cset) != NULL) 779228753Smm only_mode(bsdtar, "--format", "cru"); 780228753Smm if (bsdtar->symlink_mode != '\0') { 781228753Smm strcpy(buff, "-?"); 782228753Smm buff[1] = bsdtar->symlink_mode; 783228753Smm only_mode(bsdtar, buff, "cru"); 784228753Smm } 785228753Smm 786232153Smm /* Filename "-" implies stdio. */ 787232153Smm if (strcmp(bsdtar->filename, "-") == 0) 788232153Smm bsdtar->filename = NULL; 789232153Smm 790228753Smm switch(bsdtar->mode) { 791228753Smm case 'c': 792228753Smm tar_mode_c(bsdtar); 793228753Smm break; 794228753Smm case 'r': 795228753Smm tar_mode_r(bsdtar); 796228753Smm break; 797228753Smm case 't': 798228753Smm tar_mode_t(bsdtar); 799228753Smm break; 800228753Smm case 'u': 801228753Smm tar_mode_u(bsdtar); 802228753Smm break; 803228753Smm case 'x': 804228753Smm tar_mode_x(bsdtar); 805228753Smm break; 806228753Smm } 807228753Smm 808238856Smm archive_match_free(bsdtar->matching); 809248616Smm#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) 810228753Smm cleanup_substitution(bsdtar); 811228753Smm#endif 812248616Smm cset_free(bsdtar->cset); 813228753Smm 814228753Smm if (bsdtar->return_value != 0) 815228753Smm lafe_warnc(0, 816228753Smm "Error exit delayed from previous errors."); 817228753Smm return (bsdtar->return_value); 818228753Smm} 819228753Smm 820228753Smmstatic void 821228753Smmset_mode(struct bsdtar *bsdtar, char opt) 822228753Smm{ 823228753Smm if (bsdtar->mode != '\0' && bsdtar->mode != opt) 824228753Smm lafe_errc(1, 0, 825228753Smm "Can't specify both -%c and -%c", opt, bsdtar->mode); 826228753Smm bsdtar->mode = opt; 827228753Smm} 828228753Smm 829228753Smm/* 830228753Smm * Verify that the mode is correct. 831228753Smm */ 832228753Smmstatic void 833228753Smmonly_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) 834228753Smm{ 835228753Smm if (strchr(valid_modes, bsdtar->mode) == NULL) 836228753Smm lafe_errc(1, 0, 837228753Smm "Option %s is not permitted in mode -%c", 838228753Smm opt, bsdtar->mode); 839228753Smm} 840228753Smm 841228753Smm 842228753Smmvoid 843228753Smmusage(void) 844228753Smm{ 845228753Smm const char *p; 846228753Smm 847228753Smm p = lafe_progname; 848228753Smm 849228753Smm fprintf(stderr, "Usage:\n"); 850228753Smm fprintf(stderr, " List: %s -tf <archive-filename>\n", p); 851228753Smm fprintf(stderr, " Extract: %s -xf <archive-filename>\n", p); 852228753Smm fprintf(stderr, " Create: %s -cf <archive-filename> [filenames...]\n", p); 853228753Smm fprintf(stderr, " Help: %s --help\n", p); 854228753Smm exit(1); 855228753Smm} 856228753Smm 857228753Smmstatic void 858228753Smmversion(void) 859228753Smm{ 860228753Smm printf("bsdtar %s - %s\n", 861228753Smm BSDTAR_VERSION_STRING, 862232153Smm archive_version_string()); 863228753Smm exit(0); 864228753Smm} 865228753Smm 866228753Smmstatic const char *long_help_msg = 867228753Smm "First option must be a mode specifier:\n" 868228753Smm " -c Create -r Add/Replace -t List -u Update -x Extract\n" 869228753Smm "Common Options:\n" 870228753Smm " -b # Use # 512-byte records per I/O block\n" 871228753Smm " -f <filename> Location of archive (default " _PATH_DEFTAPE ")\n" 872228753Smm " -v Verbose\n" 873228753Smm " -w Interactive\n" 874228753Smm "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n" 875228753Smm " <file>, <dir> add these items to archive\n" 876228753Smm " -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n" 877228753Smm " --format {ustar|pax|cpio|shar} Select archive format\n" 878228753Smm " --exclude <pattern> Skip files that match pattern\n" 879228753Smm " -C <dir> Change to <dir> before processing remaining files\n" 880228753Smm " @<archive> Add entries from <archive> to output\n" 881228753Smm "List: %p -t [options] [<patterns>]\n" 882228753Smm " <patterns> If specified, list only entries that match\n" 883228753Smm "Extract: %p -x [options] [<patterns>]\n" 884228753Smm " <patterns> If specified, extract only entries that match\n" 885228753Smm " -k Keep (don't overwrite) existing files\n" 886228753Smm " -m Don't restore modification times\n" 887228753Smm " -O Write entries to stdout, don't restore to disk\n" 888228753Smm " -p Restore permissions (including ACLs, owner, file flags)\n"; 889228753Smm 890228753Smm 891228753Smm/* 892228753Smm * Note that the word 'bsdtar' will always appear in the first line 893228753Smm * of output. 894228753Smm * 895228753Smm * In particular, /bin/sh scripts that need to test for the presence 896228753Smm * of bsdtar can use the following template: 897228753Smm * 898228753Smm * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \ 899228753Smm * echo bsdtar; else echo not bsdtar; fi 900228753Smm */ 901228753Smmstatic void 902228753Smmlong_help(void) 903228753Smm{ 904228753Smm const char *prog; 905228753Smm const char *p; 906228753Smm 907228753Smm prog = lafe_progname; 908228753Smm 909228753Smm fflush(stderr); 910228753Smm 911228753Smm p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : ""; 912228753Smm printf("%s%s: manipulate archive files\n", prog, p); 913228753Smm 914228753Smm for (p = long_help_msg; *p != '\0'; p++) { 915228753Smm if (*p == '%') { 916228753Smm if (p[1] == 'p') { 917228753Smm fputs(prog, stdout); 918228753Smm p++; 919228753Smm } else 920228753Smm putchar('%'); 921228753Smm } else 922228753Smm putchar(*p); 923228753Smm } 924228753Smm version(); 925228753Smm} 926