1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2003-2008 Tim Kientzle
5 * All rights reserved.
6 */
7
8 #include "bsdtar_platform.h"
9
10 #ifdef HAVE_LIMITS_H
11 #include <limits.h>
12 #endif
13 #ifdef HAVE_SYS_PARAM_H
14 #include <sys/param.h>
15 #endif
16 #ifdef HAVE_SYS_STAT_H
17 #include <sys/stat.h>
18 #endif
19 #ifdef HAVE_COPYFILE_H
20 #include <copyfile.h>
21 #endif
22 #ifdef HAVE_ERRNO_H
23 #include <errno.h>
24 #endif
25 #ifdef HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif
28 #ifdef HAVE_LANGINFO_H
29 #include <langinfo.h>
30 #endif
31 #ifdef HAVE_LIMITS_H
32 #include <limits.h>
33 #endif
34 #ifdef HAVE_LOCALE_H
35 #include <locale.h>
36 #endif
37 #ifdef HAVE_PATHS_H
38 #include <paths.h>
39 #endif
40 #ifdef HAVE_SIGNAL_H
41 #include <signal.h>
42 #endif
43 #include <stdio.h>
44 #ifdef HAVE_STDLIB_H
45 #include <stdlib.h>
46 #endif
47 #ifdef HAVE_STRING_H
48 #include <string.h>
49 #endif
50 #ifdef HAVE_TIME_H
51 #include <time.h>
52 #endif
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56
57 #include "bsdtar.h"
58 #include "err.h"
59
60 #if ARCHIVE_VERSION_NUMBER < 4000000 && !defined(_PATH_DEFTAPE)
61 // Libarchive 4.0 and later will NOT define _PATH_DEFTAPE
62 // but will honor it if it's set in the build.
63 // Until then, we'll continue to set it by default on certain platforms:
64 #if defined(__linux)
65 #define _PATH_DEFTAPE "/dev/st0"
66 #elif defined(_WIN32) && !defined(__CYGWIN__)
67 #define _PATH_DEFTAPE "\\\\.\\tape0"
68 #elif !defined(__APPLE__)
69 #define _PATH_DEFTAPE "/dev/tape"
70 #endif
71 #endif
72
73 #define _PATH_STDIO "-"
74
75 #ifdef __MINGW32__
76 int _CRT_glob = 0; /* Disable broken CRT globbing. */
77 #endif
78
79 #if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1))
80 static volatile int siginfo_occurred;
81
82 static void
siginfo_handler(int sig)83 siginfo_handler(int sig)
84 {
85 (void)sig; /* UNUSED */
86 siginfo_occurred = 1;
87 }
88
89 int
need_report(void)90 need_report(void)
91 {
92 int r = siginfo_occurred;
93 siginfo_occurred = 0;
94 return (r);
95 }
96 #else
97 int
need_report(void)98 need_report(void)
99 {
100 return (0);
101 }
102 #endif
103
104 static __LA_NORETURN void long_help(void);
105 static void only_mode(struct bsdtar *, const char *opt,
106 const char *valid);
107 static void set_mode(struct bsdtar *, int opt);
108 static __LA_NORETURN void version(void);
109
110 /* A basic set of security flags to request from libarchive. */
111 #define SECURITY \
112 (ARCHIVE_EXTRACT_SECURE_SYMLINKS \
113 | ARCHIVE_EXTRACT_SECURE_NODOTDOT)
114
115 static char const * const vcs_files[] = {
116 /* CVS */
117 "CVS", ".cvsignore",
118 /* RCS */
119 "RCS",
120 /* SCCS */
121 "SCCS",
122 /* SVN */
123 ".svn",
124 /* git */
125 ".git", ".gitignore", ".gitattributes", ".gitmodules",
126 /* Arch */
127 ".arch-ids", "{arch}", "=RELEASE-ID", "=meta-update", "=update",
128 /* Bazaar */
129 ".bzr", ".bzrignore", ".bzrtags",
130 /* Mercurial */
131 ".hg", ".hgignore", ".hgtags",
132 /* darcs */
133 "_darcs",
134 NULL
135 };
136
137 int
main(int argc,char ** argv)138 main(int argc, char **argv)
139 {
140 struct bsdtar *bsdtar, bsdtar_storage;
141 int opt, t;
142 int compression, compression2;
143 const char *compression_name, *compression2_name;
144 const char *compress_program;
145 char *tptr, *uptr;
146 char possible_help_request;
147 char buff[16];
148 long l;
149 time_t now;
150
151 /*
152 * Use a pointer for consistency, but stack-allocated storage
153 * for ease of cleanup.
154 */
155 bsdtar = &bsdtar_storage;
156 memset(bsdtar, 0, sizeof(*bsdtar));
157 bsdtar->fd = -1; /* Mark as "unused" */
158 bsdtar->gid = -1;
159 bsdtar->uid = -1;
160 bsdtar->flags = 0;
161 compression = compression2 = '\0';
162 compression_name = compression2_name = NULL;
163 compress_program = NULL;
164 time(&now);
165
166 #if defined(HAVE_SIGACTION)
167 { /* Set up signal handling. */
168 struct sigaction sa;
169 sa.sa_handler = siginfo_handler;
170 sigemptyset(&sa.sa_mask);
171 sa.sa_flags = 0;
172 #ifdef SIGINFO
173 if (sigaction(SIGINFO, &sa, NULL))
174 lafe_errc(1, errno, "sigaction(SIGINFO) failed");
175 #endif
176 #ifdef SIGUSR1
177 /* ... and treat SIGUSR1 the same way as SIGINFO. */
178 if (sigaction(SIGUSR1, &sa, NULL))
179 lafe_errc(1, errno, "sigaction(SIGUSR1) failed");
180 #endif
181 #ifdef SIGPIPE
182 /* Ignore SIGPIPE signals. */
183 sa.sa_handler = SIG_IGN;
184 sigaction(SIGPIPE, &sa, NULL);
185 #endif
186 }
187 #endif
188
189 /* Set lafe_progname before calling lafe_warnc. */
190 lafe_setprogname(*argv, "bsdtar");
191
192 #if HAVE_SETLOCALE
193 if (setlocale(LC_ALL, "") == NULL)
194 lafe_warnc(0, "Failed to set default locale");
195 #endif
196 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
197 bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
198 #endif
199 possible_help_request = 0;
200
201 /* Look up uid of current user for future reference */
202 bsdtar->user_uid = geteuid();
203
204 /* Default: open tape drive. */
205 bsdtar->filename = getenv("TAPE");
206 #if defined(_PATH_DEFTAPE)
207 if (bsdtar->filename == NULL) {
208 #if defined(_WIN32) && !defined(__CYGWIN__)
209 int tapeExists = !_access(_PATH_DEFTAPE, 0);
210 #else
211 int tapeExists = !access(_PATH_DEFTAPE, F_OK);
212 #endif
213 if (tapeExists) {
214 bsdtar->filename = _PATH_DEFTAPE;
215 }
216 }
217 #endif
218 if (bsdtar->filename == NULL) {
219 bsdtar->filename = _PATH_STDIO;
220 }
221
222 /* Default block size settings. */
223 bsdtar->bytes_per_block = DEFAULT_BYTES_PER_BLOCK;
224 /* Allow library to default this unless user specifies -b. */
225 bsdtar->bytes_in_last_block = -1;
226
227 /* Default: preserve mod time on extract */
228 bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;
229
230 /* Default: Perform basic security checks. */
231 bsdtar->extract_flags |= SECURITY;
232
233 #ifndef _WIN32
234 /* On POSIX systems, assume --same-owner and -p when run by
235 * the root user. This doesn't make any sense on Windows. */
236 if (bsdtar->user_uid == 0) {
237 /* --same-owner */
238 bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
239 /* -p */
240 bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
241 bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
242 bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
243 bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
244 bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
245 }
246 #endif
247
248 /*
249 * Enable Mac OS "copyfile()" extension by default.
250 * This has no effect on other platforms.
251 */
252 bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
253 #ifdef COPYFILE_DISABLE_VAR
254 if (getenv(COPYFILE_DISABLE_VAR))
255 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
256 #endif
257 #if defined(__APPLE__)
258 /*
259 * On Mac OS ACLs are archived with copyfile() (--mac-metadata)
260 * Translation to NFSv4 ACLs has to be requested explicitly with --acls
261 */
262 bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
263 #endif
264
265 bsdtar->matching = archive_match_new();
266 if (bsdtar->matching == NULL)
267 lafe_errc(1, errno, "Out of memory");
268 bsdtar->cset = cset_new();
269 if (bsdtar->cset == NULL)
270 lafe_errc(1, errno, "Out of memory");
271
272 bsdtar->argv = argv;
273 bsdtar->argc = argc;
274
275 /*
276 * Comments following each option indicate where that option
277 * originated: SUSv2, POSIX, GNU tar, star, etc. If there's
278 * no such comment, then I don't know of anyone else who
279 * implements that option.
280 */
281 while ((opt = bsdtar_getopt(bsdtar)) != -1) {
282 switch (opt) {
283 case 'a': /* GNU tar */
284 bsdtar->flags |= OPTFLAG_AUTO_COMPRESS;
285 break;
286 case OPTION_ACLS: /* GNU tar */
287 bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
288 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_ACL;
289 bsdtar->flags |= OPTFLAG_ACLS;
290 break;
291 case 'B': /* GNU tar */
292 /* libarchive doesn't need this; just ignore it. */
293 break;
294 case 'b': /* SUSv2 */
295 tptr = NULL;
296 l = strtol(bsdtar->argument, &tptr, 10);
297 if (l <= 0 || l > 8192L ||
298 *(bsdtar->argument) == '\0' || tptr == NULL ||
299 *tptr != '\0') {
300 lafe_errc(1, 0, "Invalid or out of range "
301 "(1..8192) argument to -b");
302 }
303 bsdtar->bytes_per_block = 512 * (int)l;
304 /* Explicit -b forces last block size. */
305 bsdtar->bytes_in_last_block = bsdtar->bytes_per_block;
306 break;
307 case OPTION_B64ENCODE:
308 if (compression2 != '\0')
309 lafe_errc(1, 0,
310 "Can't specify both --uuencode and "
311 "--b64encode");
312 compression2 = opt;
313 compression2_name = "b64encode";
314 break;
315 case 'C': /* GNU tar */
316 if (strlen(bsdtar->argument) == 0)
317 lafe_errc(1, 0,
318 "Meaningless option: -C ''");
319
320 set_chdir(bsdtar, bsdtar->argument);
321 break;
322 case 'c': /* SUSv2 */
323 set_mode(bsdtar, opt);
324 break;
325 case OPTION_CHECK_LINKS: /* GNU tar */
326 bsdtar->flags |= OPTFLAG_WARN_LINKS;
327 break;
328 case OPTION_CHROOT: /* NetBSD */
329 bsdtar->flags |= OPTFLAG_CHROOT;
330 break;
331 case OPTION_CLEAR_NOCHANGE_FFLAGS:
332 bsdtar->extract_flags |=
333 ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS;
334 break;
335 case OPTION_EXCLUDE: /* GNU tar */
336 if (archive_match_exclude_pattern(
337 bsdtar->matching, bsdtar->argument) != ARCHIVE_OK)
338 lafe_errc(1, 0,
339 "Couldn't exclude %s", bsdtar->argument);
340 break;
341 case OPTION_EXCLUDE_VCS: /* GNU tar */
342 for(t=0; vcs_files[t]; t++) {
343 if (archive_match_exclude_pattern(
344 bsdtar->matching,
345 vcs_files[t]) != ARCHIVE_OK)
346 lafe_errc(1, 0, "Couldn't "
347 "exclude %s", vcs_files[t]);
348 }
349 break;
350 case OPTION_FFLAGS:
351 bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
352 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS;
353 bsdtar->flags |= OPTFLAG_FFLAGS;
354 break;
355 case OPTION_FORMAT: /* GNU tar, others */
356 cset_set_format(bsdtar->cset, bsdtar->argument);
357 break;
358 case 'f': /* SUSv2 */
359 bsdtar->filename = bsdtar->argument;
360 break;
361 case OPTION_GID: /* cpio */
362 tptr = NULL;
363 l = strtol(bsdtar->argument, &tptr, 10);
364 if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
365 tptr == NULL || *tptr != '\0') {
366 lafe_errc(1, 0, "Invalid argument to --gid");
367 }
368 bsdtar->gid = (int)l;
369 break;
370 case OPTION_GNAME: /* cpio */
371 bsdtar->gname = bsdtar->argument;
372 break;
373 case OPTION_GROUP: /* GNU tar */
374 tptr = NULL;
375
376 uptr = strchr(bsdtar->argument, ':');
377 if (uptr != NULL) {
378 if (uptr[1] == '\0') {
379 lafe_errc(1, 0, "Invalid argument to --group (missing id after :)");
380 }
381 uptr[0] = 0;
382 uptr++;
383 l = strtol(uptr, &tptr, 10);
384 if (l < 0 || l >= INT_MAX || *uptr == '\0' ||
385 tptr == NULL || *tptr != '\0') {
386 lafe_errc(1, 0, "Invalid argument to --group (%s is not a number)", uptr);
387 } else {
388 bsdtar->gid = (int)l;
389 }
390 bsdtar->gname = bsdtar->argument;
391 } else {
392 l = strtol(bsdtar->argument, &tptr, 10);
393 if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
394 tptr == NULL || *tptr != '\0') {
395 bsdtar->gname = bsdtar->argument;
396 } else {
397 bsdtar->gid = (int)l;
398 bsdtar->gname = "";
399 }
400 }
401 break;
402 case OPTION_GRZIP:
403 if (compression != '\0')
404 lafe_errc(1, 0,
405 "Can't specify both -%c and -%c", opt,
406 compression);
407 compression = opt;
408 compression_name = "grzip";
409 break;
410 case 'H': /* BSD convention */
411 bsdtar->symlink_mode = 'H';
412 break;
413 case 'h': /* Linux Standards Base, gtar; synonym for -L */
414 bsdtar->symlink_mode = 'L';
415 /* Hack: -h by itself is the "help" command. */
416 possible_help_request = 1;
417 break;
418 case OPTION_HELP: /* GNU tar, others */
419 long_help();
420 /* NOTREACHED*/
421 case OPTION_HFS_COMPRESSION: /* Mac OS X v10.6 or later */
422 bsdtar->extract_flags |=
423 ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED;
424 break;
425 case OPTION_IGNORE_ZEROS:
426 bsdtar->flags |= OPTFLAG_IGNORE_ZEROS;
427 break;
428 case 'I': /* GNU tar */
429 /*
430 * TODO: Allow 'names' to come from an archive,
431 * not just a text file. Design a good UI for
432 * allowing names and mode/owner to be read
433 * from an archive, with contents coming from
434 * disk. This can be used to "refresh" an
435 * archive or to design archives with special
436 * permissions without having to create those
437 * permissions on disk.
438 */
439 bsdtar->names_from_file = bsdtar->argument;
440 break;
441 case OPTION_INCLUDE:
442 /*
443 * No one else has the @archive extension, so
444 * no one else needs this to filter entries
445 * when transforming archives.
446 */
447 if (archive_match_include_pattern(bsdtar->matching,
448 bsdtar->argument) != ARCHIVE_OK)
449 lafe_errc(1, 0,
450 "Failed to add %s to inclusion list",
451 bsdtar->argument);
452 break;
453 case 'j': /* GNU tar */
454 if (compression != '\0')
455 lafe_errc(1, 0,
456 "Can't specify both -%c and -%c", opt,
457 compression);
458 compression = opt;
459 compression_name = "bzip2";
460 break;
461 case 'J': /* GNU tar 1.21 and later */
462 if (compression != '\0')
463 lafe_errc(1, 0,
464 "Can't specify both -%c and -%c", opt,
465 compression);
466 compression = opt;
467 compression_name = "xz";
468 break;
469 case 'k': /* GNU tar */
470 bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
471 break;
472 case OPTION_KEEP_NEWER_FILES: /* GNU tar */
473 bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
474 break;
475 case 'L': /* BSD convention */
476 bsdtar->symlink_mode = 'L';
477 break;
478 case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
479 /* GNU tar 1.13 used -l for --one-file-system */
480 bsdtar->flags |= OPTFLAG_WARN_LINKS;
481 break;
482 case OPTION_LRZIP:
483 case OPTION_LZ4:
484 case OPTION_LZIP: /* GNU tar beginning with 1.23 */
485 case OPTION_LZMA: /* GNU tar beginning with 1.20 */
486 case OPTION_LZOP: /* GNU tar beginning with 1.21 */
487 case OPTION_ZSTD:
488 if (compression != '\0')
489 lafe_errc(1, 0,
490 "Can't specify both -%c and -%c", opt,
491 compression);
492 compression = opt;
493 switch (opt) {
494 case OPTION_LRZIP: compression_name = "lrzip"; break;
495 case OPTION_LZ4: compression_name = "lz4"; break;
496 case OPTION_LZIP: compression_name = "lzip"; break;
497 case OPTION_LZMA: compression_name = "lzma"; break;
498 case OPTION_LZOP: compression_name = "lzop"; break;
499 case OPTION_ZSTD: compression_name = "zstd"; break;
500 }
501 break;
502 case 'm': /* SUSv2 */
503 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
504 break;
505 case OPTION_MAC_METADATA: /* Mac OS X */
506 bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
507 bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
508 bsdtar->flags |= OPTFLAG_MAC_METADATA;
509 break;
510 case 'n': /* GNU tar */
511 bsdtar->flags |= OPTFLAG_NO_SUBDIRS;
512 break;
513 /*
514 * Selecting files by time:
515 * --newer-?time='date' Only files newer than 'date'
516 * --newer-?time-than='file' Only files newer than time
517 * on specified file (useful for incremental backups)
518 */
519 case OPTION_NEWER_CTIME: /* GNU tar */
520 if (archive_match_include_date(bsdtar->matching,
521 ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
522 bsdtar->argument) != ARCHIVE_OK)
523 lafe_errc(1, 0, "Error : %s",
524 archive_error_string(bsdtar->matching));
525 break;
526 case OPTION_NEWER_CTIME_THAN:
527 if (archive_match_include_file_time(bsdtar->matching,
528 ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
529 bsdtar->argument) != ARCHIVE_OK)
530 lafe_errc(1, 0, "Error : %s",
531 archive_error_string(bsdtar->matching));
532 break;
533 case OPTION_NEWER_MTIME: /* GNU tar */
534 if (archive_match_include_date(bsdtar->matching,
535 ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
536 bsdtar->argument) != ARCHIVE_OK)
537 lafe_errc(1, 0, "Error : %s",
538 archive_error_string(bsdtar->matching));
539 break;
540 case OPTION_NEWER_MTIME_THAN:
541 if (archive_match_include_file_time(bsdtar->matching,
542 ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
543 bsdtar->argument) != ARCHIVE_OK)
544 lafe_errc(1, 0, "Error : %s",
545 archive_error_string(bsdtar->matching));
546 break;
547 case OPTION_NODUMP: /* star */
548 bsdtar->readdisk_flags |= ARCHIVE_READDISK_HONOR_NODUMP;
549 break;
550 case OPTION_NOPRESERVE_HFS_COMPRESSION:
551 /* Mac OS X v10.6 or later */
552 bsdtar->extract_flags |=
553 ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
554 break;
555 case OPTION_NO_ACLS: /* GNU tar */
556 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
557 bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
558 bsdtar->flags |= OPTFLAG_NO_ACLS;
559 break;
560 case OPTION_NO_FFLAGS:
561 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
562 bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_FFLAGS;
563 bsdtar->flags |= OPTFLAG_NO_FFLAGS;
564 break;
565 case OPTION_NO_MAC_METADATA: /* Mac OS X */
566 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
567 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
568 bsdtar->flags |= OPTFLAG_NO_MAC_METADATA;
569 break;
570 case OPTION_NO_READ_SPARSE:
571 bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_SPARSE;
572 bsdtar->flags |= OPTFLAG_NO_READ_SPARSE;
573 break;
574 case OPTION_NO_SAFE_WRITES:
575 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_SAFE_WRITES;
576 break;
577 case OPTION_NO_SAME_OWNER: /* GNU tar */
578 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
579 break;
580 case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
581 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM;
582 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
583 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
584 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
585 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
586 break;
587 case OPTION_NO_XATTRS: /* GNU tar */
588 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
589 bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_XATTR;
590 bsdtar->flags |= OPTFLAG_NO_XATTRS;
591 break;
592 case OPTION_NULL: /* GNU tar */
593 bsdtar->flags |= OPTFLAG_NULL;
594 break;
595 case OPTION_NUMERIC_OWNER: /* GNU tar */
596 bsdtar->uname = "";
597 bsdtar->gname = "";
598 bsdtar->flags |= OPTFLAG_NUMERIC_OWNER;
599 break;
600 case 'O': /* GNU tar */
601 bsdtar->flags |= OPTFLAG_STDOUT;
602 break;
603 case 'o': /* SUSv2 and GNU conflict here, but not fatally */
604 bsdtar->flags |= OPTFLAG_O;
605 break;
606 /*
607 * Selecting files by time:
608 * --older-?time='date' Only files older than 'date'
609 * --older-?time-than='file' Only files older than time
610 * on specified file
611 */
612 case OPTION_OLDER_CTIME:
613 if (archive_match_include_date(bsdtar->matching,
614 ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
615 bsdtar->argument) != ARCHIVE_OK)
616 lafe_errc(1, 0, "Error : %s",
617 archive_error_string(bsdtar->matching));
618 break;
619 case OPTION_OLDER_CTIME_THAN:
620 if (archive_match_include_file_time(bsdtar->matching,
621 ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
622 bsdtar->argument) != ARCHIVE_OK)
623 lafe_errc(1, 0, "Error : %s",
624 archive_error_string(bsdtar->matching));
625 break;
626 case OPTION_OLDER_MTIME:
627 if (archive_match_include_date(bsdtar->matching,
628 ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
629 bsdtar->argument) != ARCHIVE_OK)
630 lafe_errc(1, 0, "Error : %s",
631 archive_error_string(bsdtar->matching));
632 break;
633 case OPTION_OLDER_MTIME_THAN:
634 if (archive_match_include_file_time(bsdtar->matching,
635 ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
636 bsdtar->argument) != ARCHIVE_OK)
637 lafe_errc(1, 0, "Error : %s",
638 archive_error_string(bsdtar->matching));
639 break;
640 case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
641 bsdtar->readdisk_flags |=
642 ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS;
643 break;
644 case OPTION_OPTIONS:
645 if (bsdtar->option_options != NULL) {
646 lafe_warnc(0,
647 "Ignoring previous option '%s', separate multiple options with commas",
648 bsdtar->option_options);
649 }
650 bsdtar->option_options = bsdtar->argument;
651 break;
652 case OPTION_OWNER: /* GNU tar */
653 tptr = NULL;
654
655 uptr = strchr(bsdtar->argument, ':');
656 if (uptr != NULL) {
657 if (uptr[1] == 0) {
658 lafe_errc(1, 0, "Invalid argument to --owner (missing id after :)");
659 }
660 uptr[0] = 0;
661 uptr++;
662 l = strtol(uptr, &tptr, 10);
663 if (l < 0 || l >= INT_MAX || *uptr == '\0' ||
664 tptr == NULL || *tptr != '\0') {
665 lafe_errc(1, 0, "Invalid argument to --owner (%s is not a number)", uptr);
666 } else {
667 bsdtar->uid = (int)l;
668 }
669 bsdtar->uname = bsdtar->argument;
670 } else {
671 l = strtol(bsdtar->argument, &tptr, 10);
672 if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
673 tptr == NULL || *tptr != '\0') {
674 bsdtar->uname = bsdtar->argument;
675 } else {
676 bsdtar->uid = (int)l;
677 bsdtar->uname = "";
678 }
679 }
680 break;
681 case OPTION_MTIME: /* GNU tar */
682 bsdtar->has_mtime = 1;
683 bsdtar->mtime = archive_parse_date(now, bsdtar->argument);
684 if (bsdtar->mtime == (time_t)-1) {
685 lafe_errc(1, 0, "Invalid argument to --mtime (bad date string)");
686 }
687 break;
688 case OPTION_CLAMP_MTIME: /* GNU tar */
689 bsdtar->clamp_mtime = 1;
690 break;
691 #if 0
692 /*
693 * The common BSD -P option is not necessary, since
694 * our default is to archive symlinks, not follow
695 * them. This is convenient, as -P conflicts with GNU
696 * tar anyway.
697 */
698 case 'P': /* BSD convention */
699 /* Default behavior, no option necessary. */
700 break;
701 #endif
702 case 'P': /* GNU tar */
703 bsdtar->extract_flags &= ~SECURITY;
704 bsdtar->flags |= OPTFLAG_ABSOLUTE_PATHS;
705 break;
706 case 'p': /* GNU tar, star */
707 bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
708 bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
709 bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
710 bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
711 bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
712 break;
713 case OPTION_PASSPHRASE:
714 bsdtar->passphrase = bsdtar->argument;
715 break;
716 case OPTION_POSIX: /* GNU tar */
717 cset_set_format(bsdtar->cset, "pax");
718 break;
719 case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
720 bsdtar->flags |= OPTFLAG_FAST_READ;
721 break;
722 case 'r': /* SUSv2 */
723 set_mode(bsdtar, opt);
724 break;
725 case OPTION_READ_SPARSE:
726 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_SPARSE;
727 bsdtar->flags |= OPTFLAG_READ_SPARSE;
728 break;
729 case 'S': /* NetBSD pax-as-tar */
730 bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
731 break;
732 case 's': /* NetBSD pax-as-tar */
733 #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H)
734 add_substitution(bsdtar, bsdtar->argument);
735 #else
736 lafe_warnc(0,
737 "-s is not supported by this version of bsdtar");
738 usage();
739 #endif
740 break;
741 case OPTION_SAFE_WRITES:
742 bsdtar->extract_flags |= ARCHIVE_EXTRACT_SAFE_WRITES;
743 break;
744 case OPTION_SAME_OWNER: /* GNU tar */
745 bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
746 break;
747 case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
748 tptr = NULL;
749 l = strtol(bsdtar->argument, &tptr, 10);
750 if (l < 0 || l > 100000L || *(bsdtar->argument) == '\0' ||
751 tptr == NULL || *tptr != '\0') {
752 lafe_errc(1, 0, "Invalid argument to "
753 "--strip-components");
754 }
755 bsdtar->strip_components = (int)l;
756 break;
757 case 'T': /* GNU tar */
758 if (bsdtar->names_from_file)
759 lafe_errc(1, 0, "Multiple --files-from/-T options are not supported");
760 bsdtar->names_from_file = bsdtar->argument;
761 break;
762 case 't': /* SUSv2 */
763 set_mode(bsdtar, opt);
764 bsdtar->verbose++;
765 break;
766 case OPTION_TOTALS: /* GNU tar */
767 bsdtar->flags |= OPTFLAG_TOTALS;
768 break;
769 case 'U': /* GNU tar */
770 bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
771 bsdtar->flags |= OPTFLAG_UNLINK_FIRST;
772 break;
773 case 'u': /* SUSv2 */
774 set_mode(bsdtar, opt);
775 break;
776 case OPTION_UID: /* cpio */
777 tptr = NULL;
778 l = strtol(bsdtar->argument, &tptr, 10);
779 if (l < 0 || l >= INT_MAX || *(bsdtar->argument) == '\0' ||
780 tptr == NULL || *tptr != '\0') {
781 lafe_errc(1, 0, "Invalid argument to --uid");
782 }
783 bsdtar->uid = (int)l;
784 break;
785 case OPTION_UNAME: /* cpio */
786 bsdtar->uname = bsdtar->argument;
787 break;
788 case OPTION_UUENCODE:
789 if (compression2 != '\0')
790 lafe_errc(1, 0,
791 "Can't specify both --uuencode and "
792 "--b64encode");
793 compression2 = opt;
794 compression2_name = "uuencode";
795 break;
796 case 'v': /* SUSv2 */
797 bsdtar->verbose++;
798 break;
799 case OPTION_VERSION: /* GNU convention */
800 version();
801 /* NOTREACHED */
802 #if 0
803 /*
804 * The -W longopt feature is handled inside of
805 * bsdtar_getopt(), so -W is not available here.
806 */
807 case 'W': /* Obscure GNU convention. */
808 break;
809 #endif
810 case 'w': /* SUSv2 */
811 bsdtar->flags |= OPTFLAG_INTERACTIVE;
812 break;
813 case 'X': /* GNU tar */
814 if (archive_match_exclude_pattern_from_file(
815 bsdtar->matching, bsdtar->argument, 0)
816 != ARCHIVE_OK)
817 lafe_errc(1, 0, "Error : %s",
818 archive_error_string(bsdtar->matching));
819 break;
820 case 'x': /* SUSv2 */
821 set_mode(bsdtar, opt);
822 break;
823 case OPTION_XATTRS: /* GNU tar */
824 bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
825 bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_XATTR;
826 bsdtar->flags |= OPTFLAG_XATTRS;
827 break;
828 case 'y': /* FreeBSD version of GNU tar */
829 if (compression != '\0')
830 lafe_errc(1, 0,
831 "Can't specify both -%c and -%c", opt,
832 compression);
833 compression = opt;
834 compression_name = "bzip2";
835 break;
836 case 'Z': /* GNU tar */
837 if (compression != '\0')
838 lafe_errc(1, 0,
839 "Can't specify both -%c and -%c", opt,
840 compression);
841 compression = opt;
842 compression_name = "compress";
843 break;
844 case 'z': /* GNU tar, star, many others */
845 if (compression != '\0')
846 lafe_errc(1, 0,
847 "Can't specify both -%c and -%c", opt,
848 compression);
849 compression = opt;
850 compression_name = "gzip";
851 break;
852 case OPTION_USE_COMPRESS_PROGRAM:
853 compress_program = bsdtar->argument;
854 break;
855 default:
856 usage();
857 }
858 }
859
860 /*
861 * Sanity-check options.
862 */
863
864 /* If no "real" mode was specified, treat -h as --help. */
865 if ((bsdtar->mode == '\0') && possible_help_request) {
866 long_help();
867 }
868
869 /* Otherwise, a mode is required. */
870 if (bsdtar->mode == '\0')
871 lafe_errc(1, 0,
872 "Must specify one of -c, -r, -t, -u, -x");
873
874 /* Check boolean options only permitted in certain modes. */
875 if (bsdtar->flags & OPTFLAG_AUTO_COMPRESS) {
876 only_mode(bsdtar, "-a", "cx");
877 if (bsdtar->mode == 'x') {
878 bsdtar->flags &= ~OPTFLAG_AUTO_COMPRESS;
879 lafe_warnc(0,
880 "Ignoring option -a in mode -x");
881 }
882 }
883 if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
884 only_mode(bsdtar, "--one-file-system", "cru");
885 if (bsdtar->flags & OPTFLAG_FAST_READ)
886 only_mode(bsdtar, "--fast-read", "xt");
887 if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED)
888 only_mode(bsdtar, "--hfsCompression", "x");
889 if (bsdtar->extract_flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION)
890 only_mode(bsdtar, "--nopreserveHFSCompression", "x");
891 if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP)
892 only_mode(bsdtar, "--nodump", "cru");
893 if (bsdtar->flags & OPTFLAG_ACLS)
894 only_mode(bsdtar, "--acls", "crux");
895 if (bsdtar->flags & OPTFLAG_NO_ACLS)
896 only_mode(bsdtar, "--no-acls", "crux");
897 if (bsdtar->flags & OPTFLAG_XATTRS)
898 only_mode(bsdtar, "--xattrs", "crux");
899 if (bsdtar->flags & OPTFLAG_NO_XATTRS)
900 only_mode(bsdtar, "--no-xattrs", "crux");
901 if (bsdtar->flags & OPTFLAG_FFLAGS)
902 only_mode(bsdtar, "--fflags", "crux");
903 if (bsdtar->flags & OPTFLAG_NO_FFLAGS)
904 only_mode(bsdtar, "--no-fflags", "crux");
905 if (bsdtar->flags & OPTFLAG_MAC_METADATA)
906 only_mode(bsdtar, "--mac-metadata", "crux");
907 if (bsdtar->flags & OPTFLAG_NO_MAC_METADATA)
908 only_mode(bsdtar, "--no-mac-metadata", "crux");
909 if (bsdtar->flags & OPTFLAG_O) {
910 switch (bsdtar->mode) {
911 case 'c':
912 /*
913 * In GNU tar, -o means "old format." The
914 * "ustar" format is the closest thing
915 * supported by libarchive.
916 */
917 cset_set_format(bsdtar->cset, "ustar");
918 /* TODO: bsdtar->create_format = "v7"; */
919 break;
920 case 'x':
921 /* POSIX-compatible behavior. */
922 bsdtar->flags |= OPTFLAG_NO_OWNER;
923 bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
924 break;
925 default:
926 only_mode(bsdtar, "-o", "xc");
927 break;
928 }
929 }
930 if (bsdtar->flags & OPTFLAG_STDOUT)
931 only_mode(bsdtar, "-O", "xt");
932 if (bsdtar->flags & OPTFLAG_UNLINK_FIRST)
933 only_mode(bsdtar, "-U", "x");
934 if (bsdtar->flags & OPTFLAG_WARN_LINKS)
935 only_mode(bsdtar, "--check-links", "cr");
936
937 if ((bsdtar->flags & OPTFLAG_AUTO_COMPRESS) &&
938 cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
939 /* Ignore specified compressions if auto-compress works. */
940 compression = '\0';
941 compression2 = '\0';
942 }
943 /* Check other parameters only permitted in certain modes. */
944 if (compress_program != NULL) {
945 only_mode(bsdtar, "--use-compress-program", "cxt");
946 cset_add_filter_program(bsdtar->cset, compress_program);
947 /* Ignore specified compressions. */
948 compression = '\0';
949 compression2 = '\0';
950 }
951 if (compression != '\0') {
952 switch (compression) {
953 case 'J': case 'j': case 'y': case 'Z': case 'z':
954 strcpy(buff, "-?");
955 buff[1] = (char)compression;
956 break;
957 default:
958 strcpy(buff, "--");
959 strcat(buff, compression_name);
960 break;
961 }
962 only_mode(bsdtar, buff, "cxt");
963 cset_add_filter(bsdtar->cset, compression_name);
964 }
965 if (compression2 != '\0') {
966 strcpy(buff, "--");
967 strcat(buff, compression2_name);
968 only_mode(bsdtar, buff, "cxt");
969 cset_add_filter(bsdtar->cset, compression2_name);
970 }
971 if (cset_get_format(bsdtar->cset) != NULL)
972 only_mode(bsdtar, "--format", "cru");
973 if (bsdtar->symlink_mode != '\0') {
974 strcpy(buff, "-?");
975 buff[1] = bsdtar->symlink_mode;
976 only_mode(bsdtar, buff, "cru");
977 }
978
979 if (!bsdtar->has_mtime && bsdtar->clamp_mtime)
980 lafe_errc(1, 0,
981 "--clamp-mtime is not valid without --mtime <date>");
982
983 /*
984 * When creating an archive from a directory tree, the directory
985 * walking code will already avoid entering directories when
986 * recursive inclusion of directory content is disabled, therefore
987 * changing the matching behavior has no effect for creation modes.
988 * It is relevant for extraction or listing.
989 */
990 archive_match_set_inclusion_recursion(bsdtar->matching,
991 !(bsdtar->flags & OPTFLAG_NO_SUBDIRS));
992
993 /* Filename "-" implies stdio. */
994 if (strcmp(bsdtar->filename, "-") == 0)
995 bsdtar->filename = NULL;
996
997 switch(bsdtar->mode) {
998 case 'c':
999 tar_mode_c(bsdtar);
1000 break;
1001 case 'r':
1002 tar_mode_r(bsdtar);
1003 break;
1004 case 't':
1005 tar_mode_t(bsdtar);
1006 break;
1007 case 'u':
1008 tar_mode_u(bsdtar);
1009 break;
1010 case 'x':
1011 tar_mode_x(bsdtar);
1012 break;
1013 }
1014
1015 archive_match_free(bsdtar->matching);
1016 #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H)
1017 cleanup_substitution(bsdtar);
1018 #endif
1019 cset_free(bsdtar->cset);
1020 passphrase_free(bsdtar->ppbuff);
1021
1022 if (bsdtar->return_value != 0)
1023 lafe_warnc(0,
1024 "Error exit delayed from previous errors.");
1025 return (bsdtar->return_value);
1026 }
1027
1028 static void
set_mode(struct bsdtar * bsdtar,int opt)1029 set_mode(struct bsdtar *bsdtar, int opt)
1030 {
1031 if (bsdtar->mode != '\0' && bsdtar->mode != opt)
1032 lafe_errc(1, 0,
1033 "Can't specify both -%c and -%c", opt, bsdtar->mode);
1034 bsdtar->mode = opt;
1035 }
1036
1037 /*
1038 * Verify that the mode is correct.
1039 */
1040 static void
only_mode(struct bsdtar * bsdtar,const char * opt,const char * valid_modes)1041 only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
1042 {
1043 if (strchr(valid_modes, bsdtar->mode) == NULL)
1044 lafe_errc(1, 0,
1045 "Option %s is not permitted in mode -%c",
1046 opt, bsdtar->mode);
1047 }
1048
1049
1050 void
usage(void)1051 usage(void)
1052 {
1053 const char *p;
1054
1055 p = lafe_getprogname();
1056
1057 fprintf(stderr, "Usage:\n");
1058 fprintf(stderr, " List: %s -tf <archive-filename>\n", p);
1059 fprintf(stderr, " Extract: %s -xf <archive-filename>\n", p);
1060 fprintf(stderr, " Create: %s -cf <archive-filename> [filenames...]\n", p);
1061 fprintf(stderr, " Help: %s --help\n", p);
1062 exit(1);
1063 }
1064
1065 static void
version(void)1066 version(void)
1067 {
1068 printf("bsdtar %s - %s \n",
1069 BSDTAR_VERSION_STRING,
1070 archive_version_details());
1071 exit(0);
1072 }
1073
1074 static const char *long_help_msg =
1075 "First option must be a mode specifier:\n"
1076 " -c Create -r Add/Replace -t List -u Update -x Extract\n"
1077 "Common Options:\n"
1078 " -b # Use # 512-byte records per I/O block\n"
1079 " -f <filename> Location of archive (default " _PATH_DEFTAPE ")\n"
1080 " -v Verbose\n"
1081 " -w Interactive\n"
1082 "Create: %p -c [options] [<file> | <dir> | @<archive> | -C <dir> ]\n"
1083 " <file>, <dir> add these items to archive\n"
1084 " -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n"
1085 " --format {ustar|pax|cpio|shar} Select archive format\n"
1086 " --exclude <pattern> Skip files that match pattern\n"
1087 " --mtime <date> Set modification times for added files\n"
1088 " --clamp-mtime Only set modification times for files newer than --mtime\n"
1089 " -C <dir> Change to <dir> before processing remaining files\n"
1090 " @<archive> Add entries from <archive> to output\n"
1091 "List: %p -t [options] [<patterns>]\n"
1092 " <patterns> If specified, list only entries that match\n"
1093 "Extract: %p -x [options] [<patterns>]\n"
1094 " <patterns> If specified, extract only entries that match\n"
1095 " -k Keep (don't overwrite) existing files\n"
1096 " -m Don't restore modification times\n"
1097 " -O Write entries to stdout, don't restore to disk\n"
1098 " -p Restore permissions (including ACLs, owner, file flags)\n";
1099
1100
1101 /*
1102 * Note that the word 'bsdtar' will always appear in the first line
1103 * of output.
1104 *
1105 * In particular, /bin/sh scripts that need to test for the presence
1106 * of bsdtar can use the following template:
1107 *
1108 * if (tar --help 2>&1 | grep bsdtar >/dev/null 2>&1 ) then \
1109 * echo bsdtar; else echo not bsdtar; fi
1110 */
1111 static void
long_help(void)1112 long_help(void)
1113 {
1114 const char *prog;
1115 const char *p;
1116
1117 prog = lafe_getprogname();
1118
1119 fflush(stderr);
1120
1121 p = (strcmp(prog,"bsdtar") != 0) ? "(bsdtar)" : "";
1122 printf("%s%s: manipulate archive files\n", prog, p);
1123
1124 for (p = long_help_msg; *p != '\0'; p++) {
1125 if (*p == '%') {
1126 if (p[1] == 'p') {
1127 fputs(prog, stdout);
1128 p++;
1129 } else
1130 putchar('%');
1131 } else
1132 putchar(*p);
1133 }
1134 version();
1135 }
1136