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