示例#1
0
static void
test_for_append(struct bsdtar *bsdtar)
{
	struct stat s;

	if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
		lafe_errc(1, 0, "no files or directories specified");
	if (bsdtar->filename == NULL)
		lafe_errc(1, 0, "Cannot append to stdout.");

	if (stat(bsdtar->filename, &s) != 0)
		return;

	if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
		lafe_errc(1, 0,
		    "Cannot append to %s: not a regular file.",
		    bsdtar->filename);

/* Is this an appropriate check here on Windows? */
/*
	if (GetFileType(handle) != FILE_TYPE_DISK)
		lafe_errc(1, 0, "Cannot append");
*/

}
示例#2
0
static void
set_reader_options(struct bsdtar *bsdtar, struct archive *a)
{
	const char *reader_options;
	int r;

	(void)bsdtar; /* UNUSED */

	reader_options = getenv(ENV_READER_OPTIONS);
	if (reader_options != NULL) {
		char *p;
		/* Set default write options. */
		p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME)
		    + strlen(reader_options) + 1);
		if (p == NULL)
			lafe_errc(1, errno, "Out of memory");
		/* Prepend magic code to ignore options for
		 * a format or filters which are not added to
		 * the archive write object. */
		strncpy(p, IGNORE_WRONG_MODULE_NAME,
		    sizeof(IGNORE_WRONG_MODULE_NAME) -1);
		strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options);
		r = archive_read_set_options(a, p);
		free(p);
		if (r < ARCHIVE_WARN)
			lafe_errc(1, 0, "%s", archive_error_string(a));
		else
			archive_clear_error(a);
	}
}
示例#3
0
struct lafe_line_reader *
lafe_line_reader(const char *pathname, int nullSeparator)
{
    struct lafe_line_reader *lr;

    lr = calloc(1, sizeof(*lr));
    if (lr == NULL)
        lafe_errc(1, ENOMEM, "Can't open %s", pathname);

    lr->nullSeparator = nullSeparator;
    lr->pathname = strdup(pathname);

    if (strcmp(pathname, "-") == 0)
        lr->f = stdin;
    else
        lr->f = fopen(pathname, "r");
    if (lr->f == NULL)
        lafe_errc(1, errno, "Couldn't open %s", pathname);
    lr->buff_length = 8192;
    lr->buff = malloc(lr->buff_length);
    if (lr->buff == NULL)
        lafe_errc(1, ENOMEM, "Can't read %s", pathname);
    lr->line_start = lr->line_end = lr->buff_end = lr->buff;

    return (lr);
}
示例#4
0
static void
mode_pass(struct cpio *cpio, const char *destdir)
{
	struct lafe_line_reader *lr;
	const char *p;
	int r;

	/* Ensure target dir has a trailing '/' to simplify path surgery. */
	cpio->destdir = malloc(strlen(destdir) + 8);
	strcpy(cpio->destdir, destdir);
	if (destdir[strlen(destdir) - 1] != '/')
		strcat(cpio->destdir, "/");

	cpio->archive = archive_write_disk_new();
	if (cpio->archive == NULL)
		lafe_errc(1, 0, "Failed to allocate archive object");
	r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags);
	if (r != ARCHIVE_OK)
		lafe_errc(1, 0, "%s", archive_error_string(cpio->archive));
	cpio->linkresolver = archive_entry_linkresolver_new();
	archive_write_disk_set_standard_lookup(cpio->archive);

	cpio->archive_read_disk = archive_read_disk_new();
	if (cpio->archive_read_disk == NULL)
		lafe_errc(1, 0, "Failed to allocate archive object");
	if (cpio->option_follow_links)
		archive_read_disk_set_symlink_logical(cpio->archive_read_disk);
	else
		archive_read_disk_set_symlink_physical(cpio->archive_read_disk);
	archive_read_disk_set_standard_lookup(cpio->archive_read_disk);

	lr = lafe_line_reader("-", cpio->option_null);
	while ((p = lafe_line_reader_next(lr)) != NULL)
		file_to_archive(cpio, p);
	lafe_line_reader_free(lr);

	archive_entry_linkresolver_free(cpio->linkresolver);
	r = archive_write_close(cpio->archive);
	if (cpio->dot)
		fprintf(stderr, "\n");
	if (r != ARCHIVE_OK)
		lafe_errc(1, 0, "%s", archive_error_string(cpio->archive));

	if (!cpio->quiet) {
		int64_t blocks =
			(archive_filter_bytes(cpio->archive, 0) + 511)
			/ 512;
		fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
		    blocks == 1 ? "block" : "blocks");
	}

	archive_write_free(cpio->archive);
}
示例#5
0
文件: cpio.c 项目: 2asoft/freebsd
static void
mode_list(struct cpio *cpio)
{
	struct archive *a;
	struct archive_entry *entry;
	int r;

	a = archive_read_new();
	if (a == NULL)
		lafe_errc(1, 0, "Couldn't allocate archive object");
	archive_read_support_filter_all(a);
	archive_read_support_format_all(a);
	if (cpio->passphrase != NULL)
		r = archive_read_add_passphrase(a, cpio->passphrase);
	else
		r = archive_read_set_passphrase_callback(a, cpio,
			&passphrase_callback);
	if (r != ARCHIVE_OK)
		lafe_errc(1, 0, "%s", archive_error_string(a));

	if (archive_read_open_filename(a, cpio->filename,
					cpio->bytes_per_block))
		lafe_errc(1, archive_errno(a),
		    "%s", archive_error_string(a));
	for (;;) {
		r = archive_read_next_header(a, &entry);
		if (r == ARCHIVE_EOF)
			break;
		if (r != ARCHIVE_OK) {
			lafe_errc(1, archive_errno(a),
			    "%s", archive_error_string(a));
		}
		if (archive_match_path_excluded(cpio->matching, entry))
			continue;
		if (cpio->verbose)
			list_item_verbose(cpio, entry);
		else
			fprintf(stdout, "%s\n", archive_entry_pathname(entry));
	}
	r = archive_read_close(a);
	if (r != ARCHIVE_OK)
		lafe_errc(1, 0, "%s", archive_error_string(a));
	if (!cpio->quiet) {
		int64_t blocks = (archive_filter_bytes(a, 0) + 511)
			      / 512;
		fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
		    blocks == 1 ? "block" : "blocks");
	}
	archive_read_free(a);
	exit(0);
}
示例#6
0
文件: write.c 项目: nanakom/freebsd
void
tar_mode_c(struct bsdtar *bsdtar)
{
    struct archive *a;
    const void *filter_name;
    int r;

    if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
        lafe_errc(1, 0, "no files or directories specified");

    a = archive_write_new();

    /* Support any format that the library supports. */
    if (cset_get_format(bsdtar->cset) == NULL) {
        r = archive_write_set_format_pax_restricted(a);
        cset_set_format(bsdtar->cset, "pax restricted");
    } else {
        r = archive_write_set_format_by_name(a,
                                             cset_get_format(bsdtar->cset));
    }
    if (r != ARCHIVE_OK) {
        fprintf(stderr, "Can't use format %s: %s\n",
                cset_get_format(bsdtar->cset),
                archive_error_string(a));
        usage();
    }

    archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block);
    archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block);

    r = cset_write_add_filters(bsdtar->cset, a, &filter_name);
    if (r < ARCHIVE_WARN) {
        lafe_errc(1, 0, "Unsupported compression option --%s",
                  (const char *)filter_name);
    }

    set_writer_options(bsdtar, a);
    if (bsdtar->passphrase != NULL)
        r = archive_write_set_passphrase(a, bsdtar->passphrase);
    else
        r = archive_write_set_passphrase_callback(a, bsdtar,
                &passphrase_callback);
    if (r != ARCHIVE_OK)
        lafe_errc(1, 0, "%s", archive_error_string(a));
    if (ARCHIVE_OK != archive_write_open_filename(a, bsdtar->filename))
        lafe_errc(1, 0, "%s", archive_error_string(a));
    write_archive(a, bsdtar);
}
示例#7
0
static void
initialize_matching(struct lafe_matching **matching)
{
	*matching = calloc(sizeof(**matching), 1);
	if (*matching == NULL)
		lafe_errc(1, errno, "No memory");
}
示例#8
0
/*
 * Archive names specified in file.
 *
 * Unless --null was specified, a line containing exactly "-C" will
 * cause the next line to be a directory to pass to chdir().  If
 * --null is specified, then a line "-C" is just another filename.
 */
static void
archive_names_from_file(struct bsdtar *bsdtar, struct archive *a)
{
	struct lafe_line_reader *lr;
	const char *line;

	bsdtar->next_line_is_dir = 0;

	lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null);
	while ((line = lafe_line_reader_next(lr)) != NULL) {
		if (bsdtar->next_line_is_dir) {
			if (*line != '\0')
				set_chdir(bsdtar, line);
			else {
				lafe_warnc(0,
				    "Meaningless argument for -C: ''");
				bsdtar->return_value = 1;
			}
			bsdtar->next_line_is_dir = 0;
		} else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
			bsdtar->next_line_is_dir = 1;
		else {
			if (*line != '/')
				do_chdir(bsdtar); /* Handle a deferred -C */
			write_hierarchy(bsdtar, a, line);
		}
	}
	lafe_line_reader_free(lr);
	if (bsdtar->next_line_is_dir)
		lafe_errc(1, errno,
		    "Unexpected end of filename list; "
		    "directory expected after -C");
}
示例#9
0
文件: util.c 项目: lukecian/CMake
/*-
 * The logic here for -C <dir> attempts to avoid
 * chdir() as long as possible.  For example:
 * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
 * "-C /foo -C bar file"           needs chdir("/foo/bar")
 * "-C /foo -C bar /file1"         does not need chdir()
 * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
 *
 * The only correct way to handle this is to record a "pending" chdir
 * request and combine multiple requests intelligently until we
 * need to process a non-absolute file.  set_chdir() adds the new dir
 * to the pending list; do_chdir() actually executes any pending chdir.
 *
 * This way, programs that build tar command lines don't have to worry
 * about -C with non-existent directories; such requests will only
 * fail if the directory must be accessed.
 *
 * TODO: Make this handle Windows paths correctly.
 */
void
set_chdir(struct bsdtar *bsdtar, const char *newdir)
{
    if (newdir[0] == '/') {
        /* The -C /foo -C /bar case; dump first one. */
        free(bsdtar->pending_chdir);
        bsdtar->pending_chdir = NULL;
    }
    if (bsdtar->pending_chdir == NULL)
        /* Easy case: no previously-saved dir. */
        bsdtar->pending_chdir = strdup(newdir);
    else {
        /* The -C /foo -C bar case; concatenate */
        char *old_pending = bsdtar->pending_chdir;
        size_t old_len = strlen(old_pending);
        bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
        if (old_pending[old_len - 1] == '/')
            old_pending[old_len - 1] = '\0';
        if (bsdtar->pending_chdir != NULL)
            sprintf(bsdtar->pending_chdir, "%s/%s",
                    old_pending, newdir);
        free(old_pending);
    }
    if (bsdtar->pending_chdir == NULL)
        lafe_errc(1, errno, "No memory");
}
示例#10
0
static void
set_mode(struct bsdtar *bsdtar, char opt)
{
	if (bsdtar->mode != '\0' && bsdtar->mode != opt)
		lafe_errc(1, 0,
		    "Can't specify both -%c and -%c", opt, bsdtar->mode);
	bsdtar->mode = opt;
}
示例#11
0
/*
 * Verify that the mode is correct.
 */
static void
only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes)
{
	if (strchr(valid_modes, bsdtar->mode) == NULL)
		lafe_errc(1, 0,
		    "Option %s is not permitted in mode -%c",
		    opt, bsdtar->mode);
}
示例#12
0
static void
_cset_add_filter(struct creation_set *cset, int program, const char *filter)
{
	struct filter_set *new_ptr;
	char *new_filter;

	new_ptr = (struct filter_set *)realloc(cset->filters,
	    sizeof(*cset->filters) * (cset->filter_count + 1));
	if (new_ptr == NULL)
		lafe_errc(1, 0, "No memory");
	new_filter = strdup(filter);
	if (new_filter == NULL)
		lafe_errc(1, 0, "No memory");
	cset->filters = new_ptr;
	cset->filters[cset->filter_count].program = program;
	cset->filters[cset->filter_count].filter_name = new_filter;
	cset->filter_count++;
}
示例#13
0
static void
init_substitution(struct bsdtar *bsdtar)
{
	struct substitution *subst;

	bsdtar->substitution = subst = malloc(sizeof(*subst));
	if (subst == NULL)
		lafe_errc(1, errno, "Out of memory");
	subst->first_rule = subst->last_rule = NULL;
}
示例#14
0
void
cset_set_format(struct creation_set *cset, const char *format)
{
	char *f;

	f = strdup(format);
	if (f == NULL)
		lafe_errc(1, 0, "No memory");
	free(cset->create_format);
	cset->create_format = f;
}
示例#15
0
文件: util.c 项目: lukecian/CMake
void
do_chdir(struct bsdtar *bsdtar)
{
    if (bsdtar->pending_chdir == NULL)
        return;

    if (chdir(bsdtar->pending_chdir) != 0) {
        lafe_errc(1, 0, "could not chdir to '%s'\n",
                  bsdtar->pending_chdir);
    }
    free(bsdtar->pending_chdir);
    bsdtar->pending_chdir = NULL;
}
示例#16
0
/*
 * Lookup uname/gname from uid/gid, return NULL if no match.
 */
static const char *
lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable,
    int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id)
{
	char asnum[16];
	struct name_cache	*cache;
	const char *name;
	int slot;


	if (*name_cache_variable == NULL) {
		*name_cache_variable = malloc(sizeof(struct name_cache));
		if (*name_cache_variable == NULL)
			lafe_errc(1, ENOMEM, "No more memory");
		memset(*name_cache_variable, 0, sizeof(struct name_cache));
		(*name_cache_variable)->size = name_cache_size;
	}

	cache = *name_cache_variable;
	cache->probes++;

	slot = id % cache->size;
	if (cache->cache[slot].name != NULL) {
		if (cache->cache[slot].id == id) {
			cache->hits++;
			return (cache->cache[slot].name);
		}
		free(cache->cache[slot].name);
		cache->cache[slot].name = NULL;
	}

	if (lookup_fn(cpio, &name, id) == 0) {
		if (name == NULL || name[0] == '\0') {
			/* If lookup failed, format it as a number. */
			snprintf(asnum, sizeof(asnum), "%u", (unsigned)id);
			name = asnum;
		}
		cache->cache[slot].name = strdup(name);
		if (cache->cache[slot].name != NULL) {
			cache->cache[slot].id = id;
			return (cache->cache[slot].name);
		}
		/*
		 * Conveniently, NULL marks an empty slot, so
		 * if the strdup() fails, we've just failed to
		 * cache it.  No recovery necessary.
		 */
	}
	return (NULL);
}
示例#17
0
文件: cpio.c 项目: 2asoft/freebsd
static const char *
passphrase_callback(struct archive *a, void *_client_data)
{
	struct cpio *cpio = (struct cpio *)_client_data;
	(void)a; /* UNUSED */

	if (cpio->ppbuff == NULL) {
		cpio->ppbuff = malloc(PPBUFF_SIZE);
		if (cpio->ppbuff == NULL)
			lafe_errc(1, errno, "Out of memory");
	}
	return lafe_readpassphrase("Enter passphrase:",
		cpio->ppbuff, PPBUFF_SIZE);
}
示例#18
0
文件: write.c 项目: kamilWLca/brix
/*
 * Add an entry to the dir list for 'u' mode.
 *
 * XXX TODO: Make this fast.
 */
static void
add_dir_list(struct bsdtar *bsdtar, const char *path,
    time_t mtime_sec, int mtime_nsec)
{
	struct archive_dir_entry	*p;

	/*
	 * Search entire list to see if this file has appeared before.
	 * If it has, override the timestamp data.
	 */
	p = bsdtar->archive_dir->head;
	while (p != NULL) {
		if (strcmp(path, p->name)==0) {
			p->mtime_sec = mtime_sec;
			p->mtime_nsec = mtime_nsec;
			return;
		}
		p = p->next;
	}

	p = malloc(sizeof(*p));
	if (p == NULL)
		lafe_errc(1, ENOMEM, "Can't read archive directory");

	p->name = strdup(path);
	if (p->name == NULL)
		lafe_errc(1, ENOMEM, "Can't read archive directory");
	p->mtime_sec = mtime_sec;
	p->mtime_nsec = mtime_nsec;
	p->next = NULL;
	if (bsdtar->archive_dir->tail == NULL) {
		bsdtar->archive_dir->head = bsdtar->archive_dir->tail = p;
	} else {
		bsdtar->archive_dir->tail->next = p;
		bsdtar->archive_dir->tail = p;
	}
}
示例#19
0
char *
lafe_readpassphrase(const char *prompt, char *buf, size_t bufsiz)
{
	char *p;

	p = readpassphrase(prompt, buf, bufsiz, RPP_ECHO_OFF);
	if (p == NULL) {
		switch (errno) {
		case EINTR:
			break;
		default:
			lafe_errc(1, errno, "Couldn't read passphrase");
			break;
		}
	}
	return (p);
}
示例#20
0
static int
unmatched_inclusions_warn(struct archive *matching, const char *msg)
{
	const char *p;
	int r;

	if (matching == NULL)
		return (0);

	while ((r = archive_match_path_unmatched_inclusions_next(
	    matching, &p)) == ARCHIVE_OK)
		lafe_warnc(0, "%s: %s", p, msg);
	if (r == ARCHIVE_FATAL)
		lafe_errc(1, errno, "Out of memory");

	return (archive_match_path_unmatched_inclusions(matching));
}
示例#21
0
static void
add_pattern(struct match **list, const char *pattern)
{
	struct match *match;
	size_t len;

	len = strlen(pattern);
	match = malloc(sizeof(*match) + len + 1);
	if (match == NULL)
		lafe_errc(1, errno, "Out of memory");
	strcpy(match->pattern, pattern);
	/* Both "foo/" and "foo" should match "foo/bar". */
	if (len && match->pattern[len - 1] == '/')
		match->pattern[len - 1] = '\0';
	match->next = *list;
	*list = match;
	match->matches = 0;
}
示例#22
0
文件: read.c 项目: AhmadTux/freebsd
void
tar_mode_x(struct bsdtar *bsdtar)
{
	struct archive *writer;

	writer = archive_write_disk_new();
	if (writer == NULL)
		lafe_errc(1, ENOMEM, "Cannot allocate disk writer object");
	if (!bsdtar->option_numeric_owner)
		archive_write_disk_set_standard_lookup(writer);
	archive_write_disk_set_options(writer, bsdtar->extract_flags);

	read_archive(bsdtar, 'x', writer);

	if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
		bsdtar->return_value = 1;
	archive_write_free(writer);
}
示例#23
0
static void
realloc_strcat(char **str, const char *append)
{
    char *new_str;
    size_t old_len;

    if (*str == NULL)
        old_len = 0;
    else
        old_len = strlen(*str);

    new_str = malloc(old_len + strlen(append) + 1);
    if (new_str == NULL)
        lafe_errc(1, errno, "Out of memory");
    memcpy(new_str, *str, old_len);
    strcpy(new_str + old_len, append);
    free(*str);
    *str = new_str;
}
示例#24
0
文件: write.c 项目: nanakom/freebsd
/*
 * Copy from specified archive to current archive.  Returns non-zero
 * for write errors (which force us to terminate the entire archiving
 * operation).  If there are errors reading the input archive, we set
 * bsdtar->return_value but return zero, so the overall archiving
 * operation will complete and return non-zero.
 */
static int
append_archive_filename(struct bsdtar *bsdtar, struct archive *a,
                        const char *raw_filename)
{
    struct archive *ina;
    const char *filename = raw_filename;
    int rc;

    if (strcmp(filename, "-") == 0)
        filename = NULL; /* Library uses NULL for stdio. */

    ina = archive_read_new();
    archive_read_support_format_all(ina);
    archive_read_support_filter_all(ina);
    set_reader_options(bsdtar, ina);
    archive_read_set_options(ina, "mtree:checkfs");
    if (bsdtar->passphrase != NULL)
        rc = archive_read_add_passphrase(a, bsdtar->passphrase);
    else
        rc = archive_read_set_passphrase_callback(ina, bsdtar,
                &passphrase_callback);
    if (rc != ARCHIVE_OK)
        lafe_errc(1, 0, "%s", archive_error_string(a));
    if (archive_read_open_filename(ina, filename,
                                   bsdtar->bytes_per_block)) {
        lafe_warnc(0, "%s", archive_error_string(ina));
        bsdtar->return_value = 1;
        return (0);
    }

    rc = append_archive(bsdtar, a, ina);

    if (rc != ARCHIVE_OK) {
        lafe_warnc(0, "Error reading archive %s: %s",
                   raw_filename, archive_error_string(ina));
        bsdtar->return_value = 1;
    }
    archive_read_free(ina);

    return (rc);
}
示例#25
0
static void
realloc_strncat(char **str, const char *append, size_t len)
{
	char *new_str;
	size_t old_len;

	if (*str == NULL)
		old_len = 0;
	else
		old_len = strlen(*str);

	new_str = malloc(old_len + len + 1);
	if (new_str == NULL)
		lafe_errc(1, errno, "Out of memory");
	if (*str != NULL)
		memcpy(new_str, *str, old_len);
	memcpy(new_str + old_len, append, len);
	new_str[old_len + len] = '\0';
	free(*str);
	*str = new_str;
}
示例#26
0
void
add_substitution(struct bsdtar *bsdtar, const char *rule_text)
{
	struct subst_rule *rule;
	struct substitution *subst;
	const char *end_pattern, *start_subst;
	char *pattern;
	int r;

	if ((subst = bsdtar->substitution) == NULL) {
		init_substitution(bsdtar);
		subst = bsdtar->substitution;
	}

	rule = malloc(sizeof(*rule));
	if (rule == NULL)
		lafe_errc(1, errno, "Out of memory");
	rule->next = NULL;

	if (subst->last_rule == NULL)
		subst->first_rule = rule;
	else
		subst->last_rule->next = rule;
	subst->last_rule = rule;

	if (*rule_text == '\0')
		lafe_errc(1, 0, "Empty replacement string");
	end_pattern = strchr(rule_text + 1, *rule_text);
	if (end_pattern == NULL)
		lafe_errc(1, 0, "Invalid replacement string");

	pattern = malloc(end_pattern - rule_text);
	if (pattern == NULL)
		lafe_errc(1, errno, "Out of memory");
	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
	pattern[end_pattern - rule_text - 1] = '\0';

	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
		char buf[80];
		regerror(r, &rule->re, buf, sizeof(buf));
		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
	}
	free(pattern);

	start_subst = end_pattern + 1;
	end_pattern = strchr(start_subst, *rule_text);
	if (end_pattern == NULL)
		lafe_errc(1, 0, "Invalid replacement string");

	rule->result = malloc(end_pattern - start_subst + 1);
	if (rule->result == NULL)
		lafe_errc(1, errno, "Out of memory");
	memcpy(rule->result, start_subst, end_pattern - start_subst);
	rule->result[end_pattern - start_subst] = '\0';

	/* Defaults */
	rule->global = 0; /* Don't do multiple replacements. */
	rule->print = 0; /* Don't print. */
	rule->regular = 1; /* Rewrite regular filenames. */
	rule->symlink = 1; /* Rewrite symlink targets. */
	rule->hardlink = 1; /* Rewrite hardlink targets. */

	while (*++end_pattern) {
		switch (*end_pattern) {
		case 'g':
		case 'G':
			rule->global = 1;
			break;
		case 'h':
			rule->hardlink = 1;
			break;
		case 'H':
			rule->hardlink = 0;
			break;
		case 'p':
		case 'P':
			rule->print = 1;
			break;
		case 'r':
			rule->regular = 1;
			break;
		case 'R':
			rule->regular = 0;
			break;
		case 's':
			rule->symlink = 1;
			break;
		case 'S':
			rule->symlink = 0;
			break;
		default:
			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
		}
	}
}
示例#27
0
/*
 * Build a creation set by a file name suffix.
 */
int
cset_auto_compress(struct creation_set *cset, const char *filename)
{
	struct filter_set *old_filters;
	char *name, *p;
	const char *code;
	int old_filter_count;

	name = strdup(filename);
	if (name == NULL)
		lafe_errc(1, 0, "No memory");
	/* Save previous filters. */
	old_filters = cset->filters;
	old_filter_count = cset->filter_count;
	cset->filters = NULL;
	cset->filter_count = 0;

	for (;;) {
		/* Get the suffix. */
		p = strrchr(name, '.');
		if (p == NULL)
			break;
		/* Suppose it indicates compression/filter type
		 * such as ".gz". */
		code = get_filter_code(p);
		if (code != NULL) {
			cset_add_filter(cset, code);
			*p = '\0';
			continue;
		}
		/* Suppose it indicates format type such as ".tar". */
		code = get_format_code(p);
		if (code != NULL) {
			cset_set_format(cset, code);
			break;
		}
		/* Suppose it indicates alias such as ".tgz". */
		code = decompose_alias(p);
		if (code == NULL)
			break;
		/* Replace the suffix. */
		*p = '\0';
		name = realloc(name, strlen(name) + strlen(code) + 1);
		if (name == NULL)
			lafe_errc(1, 0, "No memory");
		strcat(name, code);
	}
	free(name);
	if (cset->filters) {
		struct filter_set *v;
		int i, r;

		/* Release previous filters. */
		_cleanup_filters(old_filters, old_filter_count);

		v = malloc(sizeof(*v) * cset->filter_count);
		if (v == NULL)
			lafe_errc(1, 0, "No memory");
		/* Reverse filter sequence. */
		for (i = 0, r = cset->filter_count; r > 0; )
			v[i++] = cset->filters[--r];
		free(cset->filters);
		cset->filters = v;
		return (1);
	} else {
		/* Put previous filters back. */
		cset->filters = old_filters;
		cset->filter_count = old_filter_count;
		return (0);
	}
}
示例#28
0
/*-
 * The logic here for -C <dir> attempts to avoid
 * chdir() as long as possible.  For example:
 * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
 * "-C /foo -C bar file"           needs chdir("/foo/bar")
 * "-C /foo -C bar /file1"         does not need chdir()
 * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
 *
 * The only correct way to handle this is to record a "pending" chdir
 * request and combine multiple requests intelligently until we
 * need to process a non-absolute file.  set_chdir() adds the new dir
 * to the pending list; do_chdir() actually executes any pending chdir.
 *
 * This way, programs that build tar command lines don't have to worry
 * about -C with non-existent directories; such requests will only
 * fail if the directory must be accessed.
 *
 */
void
set_chdir(struct bsdtar *bsdtar, const char *newdir)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
	if (newdir[0] == '/' || newdir[0] == '\\' ||
	    /* Detect this type, for example, "C:\" or "C:/" */
	    (((newdir[0] >= 'a' && newdir[0] <= 'z') ||
	      (newdir[0] >= 'A' && newdir[0] <= 'Z')) &&
	    newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) {
#else
	if (newdir[0] == '/') {
#endif
		/* The -C /foo -C /bar case; dump first one. */
		free(bsdtar->pending_chdir);
		bsdtar->pending_chdir = NULL;
	}
	if (bsdtar->pending_chdir == NULL)
		/* Easy case: no previously-saved dir. */
		bsdtar->pending_chdir = strdup(newdir);
	else {
		/* The -C /foo -C bar case; concatenate */
		char *old_pending = bsdtar->pending_chdir;
		size_t old_len = strlen(old_pending);
		bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
		if (old_pending[old_len - 1] == '/')
			old_pending[old_len - 1] = '\0';
		if (bsdtar->pending_chdir != NULL)
			sprintf(bsdtar->pending_chdir, "%s/%s",
			    old_pending, newdir);
		free(old_pending);
	}
	if (bsdtar->pending_chdir == NULL)
		lafe_errc(1, errno, "No memory");
}

void
do_chdir(struct bsdtar *bsdtar)
{
	if (bsdtar->pending_chdir == NULL)
		return;

	if (chdir(bsdtar->pending_chdir) != 0) {
		lafe_errc(1, 0, "could not chdir to '%s'\n",
		    bsdtar->pending_chdir);
	}
	free(bsdtar->pending_chdir);
	bsdtar->pending_chdir = NULL;
}

static const char *
strip_components(const char *p, int elements)
{
	/* Skip as many elements as necessary. */
	while (elements > 0) {
		switch (*p++) {
		case '/':
#if defined(_WIN32) && !defined(__CYGWIN__)
		case '\\': /* Support \ path sep on Windows ONLY. */
#endif
			elements--;
			break;
		case '\0':
			/* Path is too short, skip it. */
			return (NULL);
		}
	}

	/* Skip any / characters.  This handles short paths that have
	 * additional / termination.  This also handles the case where
	 * the logic above stops in the middle of a duplicate //
	 * sequence (which would otherwise get converted to an
	 * absolute path). */
	for (;;) {
		switch (*p) {
		case '/':
#if defined(_WIN32) && !defined(__CYGWIN__)
		case '\\': /* Support \ path sep on Windows ONLY. */
#endif
			++p;
			break;
		case '\0':
			return (NULL);
		default:
			return (p);
		}
	}
}

static void
warn_strip_leading_char(struct bsdtar *bsdtar, const char *c)
{
	if (!bsdtar->warned_lead_slash) {
		lafe_warnc(0,
			   "Removing leading '%c' from member names",
			   c[0]);
		bsdtar->warned_lead_slash = 1;
	}
}

static void
warn_strip_drive_letter(struct bsdtar *bsdtar)
{
	if (!bsdtar->warned_lead_slash) {
		lafe_warnc(0,
			   "Removing leading drive letter from "
			   "member names");
		bsdtar->warned_lead_slash = 1;
	}
}

/*
 * Convert absolute path to non-absolute path by skipping leading
 * absolute path prefixes.
 */
static const char*
strip_absolute_path(struct bsdtar *bsdtar, const char *p)
{
	const char *rp;

	/* Remove leading "//./" or "//?/" or "//?/UNC/"
	 * (absolute path prefixes used by Windows API) */
	if ((p[0] == '/' || p[0] == '\\') &&
	    (p[1] == '/' || p[1] == '\\') &&
	    (p[2] == '.' || p[2] == '?') &&
	    (p[3] == '/' || p[3] == '\\'))
	{
		if (p[2] == '?' &&
		    (p[4] == 'U' || p[4] == 'u') &&
		    (p[5] == 'N' || p[5] == 'n') &&
		    (p[6] == 'C' || p[6] == 'c') &&
		    (p[7] == '/' || p[7] == '\\'))
			p += 8;
		else
			p += 4;
		warn_strip_drive_letter(bsdtar);
	}

	/* Remove multiple leading slashes and Windows drive letters. */
	do {
		rp = p;
		if (((p[0] >= 'a' && p[0] <= 'z') ||
		     (p[0] >= 'A' && p[0] <= 'Z')) &&
		    p[1] == ':') {
			p += 2;
			warn_strip_drive_letter(bsdtar);
		}

		/* Remove leading "/../", "/./", "//", etc. */
		while (p[0] == '/' || p[0] == '\\') {
			if (p[1] == '.' &&
			    p[2] == '.' &&
			    (p[3] == '/' || p[3] == '\\')) {
				p += 3; /* Remove "/..", leave "/" for next pass. */
			} else if (p[1] == '.' &&
				   (p[2] == '/' || p[2] == '\\')) {
				p += 2; /* Remove "/.", leave "/" for next pass. */
			} else
				p += 1; /* Remove "/". */
			warn_strip_leading_char(bsdtar, rp);
		}
	} while (rp != p);

	return (p);
}

/*
 * Handle --strip-components and any future path-rewriting options.
 * Returns non-zero if the pathname should not be extracted.
 *
 * Note: The rewrites are applied uniformly to pathnames and hardlink
 * names but not to symlink bodies.  This is deliberate: Symlink
 * bodies are not necessarily filenames.  Even when they are, they
 * need to be interpreted relative to the directory containing them,
 * so simple rewrites like this are rarely appropriate.
 *
 * TODO: Support pax-style regex path rewrites.
 */
int
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
	const char *name = archive_entry_pathname(entry);
	const char *original_name = name;
	const char *hardlinkname = archive_entry_hardlink(entry);
	const char *original_hardlinkname = hardlinkname;
#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
	char *subst_name;
	int r;

	/* Apply user-specified substitution to pathname. */
	r = apply_substitution(bsdtar, name, &subst_name, 0, 0);
	if (r == -1) {
		lafe_warnc(0, "Invalid substitution, skipping entry");
		return 1;
	}
	if (r == 1) {
		archive_entry_copy_pathname(entry, subst_name);
		if (*subst_name == '\0') {
			free(subst_name);
			return -1;
		} else
			free(subst_name);
		name = archive_entry_pathname(entry);
		original_name = name;
	}

	/* Apply user-specified substitution to hardlink target. */
	if (hardlinkname != NULL) {
		r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1);
		if (r == -1) {
			lafe_warnc(0, "Invalid substitution, skipping entry");
			return 1;
		}
		if (r == 1) {
			archive_entry_copy_hardlink(entry, subst_name);
			free(subst_name);
		}
		hardlinkname = archive_entry_hardlink(entry);
		original_hardlinkname = hardlinkname;
	}

	/* Apply user-specified substitution to symlink body. */
	if (archive_entry_symlink(entry) != NULL) {
		r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0);
		if (r == -1) {
			lafe_warnc(0, "Invalid substitution, skipping entry");
			return 1;
		}
		if (r == 1) {
			archive_entry_copy_symlink(entry, subst_name);
			free(subst_name);
		}
	}
#endif

	/* Strip leading dir names as per --strip-components option. */
	if (bsdtar->strip_components > 0) {
		name = strip_components(name, bsdtar->strip_components);
		if (name == NULL)
			return (1);

		if (hardlinkname != NULL) {
			hardlinkname = strip_components(hardlinkname,
			    bsdtar->strip_components);
			if (hardlinkname == NULL)
				return (1);
		}
	}

	if (!bsdtar->option_absolute_paths) {
		/* By default, don't write or restore absolute pathnames. */
		name = strip_absolute_path(bsdtar, name);
		if (*name == '\0')
			name = ".";

		if (hardlinkname != NULL) {
			hardlinkname = strip_absolute_path(bsdtar, hardlinkname);
			if (*hardlinkname == '\0')
				return (1);
		}
	} else {
		/* Strip redundant leading '/' characters. */
		while (name[0] == '/' && name[1] == '/')
			name++;
	}

	/* Replace name in archive_entry. */
	if (name != original_name) {
		archive_entry_copy_pathname(entry, name);
	}
	if (hardlinkname != original_hardlinkname) {
		archive_entry_copy_hardlink(entry, hardlinkname);
	}
	return (0);
}

/*
 * It would be nice to just use printf() for formatting large numbers,
 * but the compatibility problems are quite a headache.  Hence the
 * following simple utility function.
 */
const char *
tar_i64toa(int64_t n0)
{
	static char buff[24];
	uint64_t n = n0 < 0 ? -n0 : n0;
	char *p = buff + sizeof(buff);

	*--p = '\0';
	do {
		*--p = '0' + (int)(n % 10);
	} while (n /= 10);
	if (n0 < 0)
		*--p = '-';
	return p;
}

/*
 * Like strcmp(), but try to be a little more aware of the fact that
 * we're comparing two paths.  Right now, it just handles leading
 * "./" and trailing '/' specially, so that "a/b/" == "./a/b"
 *
 * TODO: Make this better, so that "./a//b/./c/" == "a/b/c"
 * TODO: After this works, push it down into libarchive.
 * TODO: Publish the path normalization routines in libarchive so
 * that bsdtar can normalize paths and use fast strcmp() instead
 * of this.
 *
 * Note: This is currently only used within write.c, so should
 * not handle \ path separators.
 */

int
pathcmp(const char *a, const char *b)
{
	/* Skip leading './' */
	if (a[0] == '.' && a[1] == '/' && a[2] != '\0')
		a += 2;
	if (b[0] == '.' && b[1] == '/' && b[2] != '\0')
		b += 2;
	/* Find the first difference, or return (0) if none. */
	while (*a == *b) {
		if (*a == '\0')
			return (0);
		a++;
		b++;
	}
	/*
	 * If one ends in '/' and the other one doesn't,
	 * they're the same.
	 */
	if (a[0] == '/' && a[1] == '\0' && b[0] == '\0')
		return (0);
	if (a[0] == '\0' && b[0] == '/' && b[1] == '\0')
		return (0);
	/* They're really different, return the correct sign. */
	return (*(const unsigned char *)a - *(const unsigned char *)b);
}

#define PPBUFF_SIZE 1024
const char *
passphrase_callback(struct archive *a, void *_client_data)
{
	struct bsdtar *bsdtar = (struct bsdtar *)_client_data;
	(void)a; /* UNUSED */

	if (bsdtar->ppbuff == NULL) {
		bsdtar->ppbuff = malloc(PPBUFF_SIZE);
		if (bsdtar->ppbuff == NULL)
			lafe_errc(1, errno, "Out of memory");
	}
	return lafe_readpassphrase("Enter passphrase:",
		bsdtar->ppbuff, PPBUFF_SIZE);
}

void
passphrase_free(char *ppbuff)
{
	if (ppbuff != NULL) {
		memset(ppbuff, 0, PPBUFF_SIZE);
		free(ppbuff);
	}
}

/*
 * Display information about the current file.
 *
 * The format here roughly duplicates the output of 'ls -l'.
 * This is based on SUSv2, where 'tar tv' is documented as
 * listing additional information in an "unspecified format,"
 * and 'pax -l' is documented as using the same format as 'ls -l'.
 */
void
list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
{
	char			 tmp[100];
	size_t			 w;
	const char		*p;
	const char		*fmt;
	time_t			 tim;
	static time_t		 now;

	/*
	 * We avoid collecting the entire list in memory at once by
	 * listing things as we see them.  However, that also means we can't
	 * just pre-compute the field widths.  Instead, we start with guesses
	 * and just widen them as necessary.  These numbers are completely
	 * arbitrary.
	 */
	if (!bsdtar->u_width) {
		bsdtar->u_width = 6;
		bsdtar->gs_width = 13;
	}
	if (!now)
		time(&now);
	fprintf(out, "%s %d ",
	    archive_entry_strmode(entry),
	    archive_entry_nlink(entry));

	/* Use uname if it's present, else uid. */
	p = archive_entry_uname(entry);
	if ((p == NULL) || (*p == '\0')) {
		sprintf(tmp, "%lu ",
		    (unsigned long)archive_entry_uid(entry));
		p = tmp;
	}
	w = strlen(p);
	if (w > bsdtar->u_width)
		bsdtar->u_width = w;
	fprintf(out, "%-*s ", (int)bsdtar->u_width, p);

	/* Use gname if it's present, else gid. */
	p = archive_entry_gname(entry);
	if (p != NULL && p[0] != '\0') {
		fprintf(out, "%s", p);
		w = strlen(p);
	} else {
		sprintf(tmp, "%lu",
		    (unsigned long)archive_entry_gid(entry));
		w = strlen(tmp);
		fprintf(out, "%s", tmp);
	}

	/*
	 * Print device number or file size, right-aligned so as to make
	 * total width of group and devnum/filesize fields be gs_width.
	 * If gs_width is too small, grow it.
	 */
	if (archive_entry_filetype(entry) == AE_IFCHR
	    || archive_entry_filetype(entry) == AE_IFBLK) {
		sprintf(tmp, "%lu,%lu",
		    (unsigned long)archive_entry_rdevmajor(entry),
		    (unsigned long)archive_entry_rdevminor(entry));
	} else {
		strcpy(tmp, tar_i64toa(archive_entry_size(entry)));
	}
	if (w + strlen(tmp) >= bsdtar->gs_width)
		bsdtar->gs_width = w+strlen(tmp)+1;
	fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);

	/* Format the time using 'ls -l' conventions. */
	tim = archive_entry_mtime(entry);
#define	HALF_YEAR (time_t)365 * 86400 / 2
#if defined(_WIN32) && !defined(__CYGWIN__)
#define	DAY_FMT  "%d"  /* Windows' strftime function does not support %e format. */
#else
#define	DAY_FMT  "%e"  /* Day number without leading zeros */
#endif
	if (tim < now - HALF_YEAR || tim > now + HALF_YEAR)
		fmt = bsdtar->day_first ? DAY_FMT " %b  %Y" : "%b " DAY_FMT "  %Y";
	else
		fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M";
	strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
	fprintf(out, " %s ", tmp);
	safe_fprintf(out, "%s", archive_entry_pathname(entry));

	/* Extra information for links. */
	if (archive_entry_hardlink(entry)) /* Hard link */
		safe_fprintf(out, " link to %s",
		    archive_entry_hardlink(entry));
	else if (archive_entry_symlink(entry)) /* Symbolic link */
		safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
}
示例#29
0
int
main(int argc, char **argv)
{
    struct bsdtar       *bsdtar, bsdtar_storage;
    int          opt, t;
    char             option_o;
    char             possible_help_request;
    char             buff[16];
    time_t           now;

    /*
     * Use a pointer for consistency, but stack-allocated storage
     * for ease of cleanup.
     */
    _bsdtar = bsdtar = &bsdtar_storage;
    memset(bsdtar, 0, sizeof(*bsdtar));
    bsdtar->fd = -1; /* Mark as "unused" */
    option_o = 0;

#if defined(SIGINFO) || defined(SIGUSR1)
    { /* Catch SIGINFO and SIGUSR1, if they exist. */
        struct sigaction sa;
        sa.sa_handler = siginfo_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
#ifdef SIGINFO
        if (sigaction(SIGINFO, &sa, NULL))
            lafe_errc(1, errno, "sigaction(SIGINFO) failed");
#endif
#ifdef SIGUSR1
        /* ... and treat SIGUSR1 the same way as SIGINFO. */
        if (sigaction(SIGUSR1, &sa, NULL))
            lafe_errc(1, errno, "sigaction(SIGUSR1) failed");
#endif
    }
#endif


    /* Need lafe_progname before calling lafe_warnc. */
    if (*argv == NULL)
        lafe_progname = "bsdtar";
    else {
#if defined(_WIN32) && !defined(__CYGWIN__)
        lafe_progname = strrchr(*argv, '\\');
#else
        lafe_progname = strrchr(*argv, '/');
#endif
        if (lafe_progname != NULL)
            lafe_progname++;
        else
            lafe_progname = *argv;
    }

    time(&now);

#if HAVE_SETLOCALE
    if (setlocale(LC_ALL, "") == NULL)
        lafe_warnc(0, "Failed to set default locale");
#endif
#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
    bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
#endif
    possible_help_request = 0;

    /* Look up uid of current user for future reference */
    bsdtar->user_uid = geteuid();

    /* Default: open tape drive. */
    bsdtar->filename = getenv("TAPE");
    if (bsdtar->filename == NULL)
        bsdtar->filename = _PATH_DEFTAPE;

    /* Default: preserve mod time on extract */
    bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;

    /* Default: Perform basic security checks. */
    bsdtar->extract_flags |= SECURITY;

#ifndef _WIN32
    /* On POSIX systems, assume --same-owner and -p when run by
     * the root user.  This doesn't make any sense on Windows. */
    if (bsdtar->user_uid == 0) {
        /* --same-owner */
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
        /* -p */
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
        bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
    }
#endif

    bsdtar->argv = argv;
    bsdtar->argc = argc;

    /*
     * Comments following each option indicate where that option
     * originated:  SUSv2, POSIX, GNU tar, star, etc.  If there's
     * no such comment, then I don't know of anyone else who
     * implements that option.
     */
    while ((opt = bsdtar_getopt(bsdtar)) != -1) {
        switch (opt) {
        case 'B': /* GNU tar */
            /* libarchive doesn't need this; just ignore it. */
            break;
        case 'b': /* SUSv2 */
            t = atoi(bsdtar->optarg);
            if (t <= 0 || t > 1024)
                lafe_errc(1, 0,
                    "Argument to -b is out of range (1..1024)");
            bsdtar->bytes_per_block = 512 * t;
            break;
        case 'C': /* GNU tar */
            set_chdir(bsdtar, bsdtar->optarg);
            break;
        case 'c': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case OPTION_CHECK_LINKS: /* GNU tar */
            bsdtar->option_warn_links = 1;
            break;
        case OPTION_CHROOT: /* NetBSD */
            bsdtar->option_chroot = 1;
            break;
        case OPTION_EXCLUDE: /* GNU tar */
            if (lafe_exclude(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "Couldn't exclude %s\n", bsdtar->optarg);
            break;
        case OPTION_FORMAT: /* GNU tar, others */
            bsdtar->create_format = bsdtar->optarg;
            break;
        case OPTION_OPTIONS:
            bsdtar->option_options = bsdtar->optarg;
            break;
        case 'f': /* SUSv2 */
            bsdtar->filename = bsdtar->optarg;
            if (strcmp(bsdtar->filename, "-") == 0)
                bsdtar->filename = NULL;
            break;
        case 'H': /* BSD convention */
            bsdtar->symlink_mode = 'H';
            break;
        case 'h': /* Linux Standards Base, gtar; synonym for -L */
            bsdtar->symlink_mode = 'L';
            /* Hack: -h by itself is the "help" command. */
            possible_help_request = 1;
            break;
        case OPTION_HELP: /* GNU tar, others */
            long_help();
            exit(0);
            break;
        case 'I': /* GNU tar */
            /*
             * TODO: Allow 'names' to come from an archive,
             * not just a text file.  Design a good UI for
             * allowing names and mode/owner to be read
             * from an archive, with contents coming from
             * disk.  This can be used to "refresh" an
             * archive or to design archives with special
             * permissions without having to create those
             * permissions on disk.
             */
            bsdtar->names_from_file = bsdtar->optarg;
            break;
        case OPTION_INCLUDE:
            /*
             * Noone else has the @archive extension, so
             * noone else needs this to filter entries
             * when transforming archives.
             */
            if (lafe_include(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "Failed to add %s to inclusion list",
                    bsdtar->optarg);
            break;
        case 'j': /* GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'J': /* GNU tar 1.21 and later */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'k': /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
            break;
        case OPTION_KEEP_NEWER_FILES: /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
            break;
        case 'L': /* BSD convention */
            bsdtar->symlink_mode = 'L';
            break;
            case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
            /* GNU tar 1.13  used -l for --one-file-system */
            bsdtar->option_warn_links = 1;
            break;
        case OPTION_LZMA:
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'm': /* SUSv2 */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
            break;
        case 'n': /* GNU tar */
            bsdtar->option_no_subdirs = 1;
            break;
            /*
         * Selecting files by time:
         *    --newer-?time='date' Only files newer than 'date'
         *    --newer-?time-than='file' Only files newer than time
         *         on specified file (useful for incremental backups)
         * TODO: Add corresponding "older" options to reverse these.
         */
        case OPTION_NEWER_CTIME: /* GNU tar */
            bsdtar->newer_ctime_sec = get_date(now, bsdtar->optarg);
            break;
        case OPTION_NEWER_CTIME_THAN:
            {
                struct stat st;
                if (stat(bsdtar->optarg, &st) != 0)
                    lafe_errc(1, 0,
                        "Can't open file %s", bsdtar->optarg);
                bsdtar->newer_ctime_sec = st.st_ctime;
                bsdtar->newer_ctime_nsec =
                    ARCHIVE_STAT_CTIME_NANOS(&st);
            }
            break;
        case OPTION_NEWER_MTIME: /* GNU tar */
            bsdtar->newer_mtime_sec = get_date(now, bsdtar->optarg);
            break;
        case OPTION_NEWER_MTIME_THAN:
            {
                struct stat st;
                if (stat(bsdtar->optarg, &st) != 0)
                    lafe_errc(1, 0,
                        "Can't open file %s", bsdtar->optarg);
                bsdtar->newer_mtime_sec = st.st_mtime;
                bsdtar->newer_mtime_nsec =
                    ARCHIVE_STAT_MTIME_NANOS(&st);
            }
            break;
        case OPTION_NODUMP: /* star */
            bsdtar->option_honor_nodump = 1;
            break;
        case OPTION_NO_SAME_OWNER: /* GNU tar */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
            break;
        case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
            break;
        case OPTION_NULL: /* GNU tar */
            bsdtar->option_null++;
            break;
        case OPTION_NUMERIC_OWNER: /* GNU tar */
            bsdtar->option_numeric_owner++;
            break;
        case 'O': /* GNU tar */
            bsdtar->option_stdout = 1;
            break;
        case 'o': /* SUSv2 and GNU conflict here, but not fatally */
            option_o = 1; /* Record it and resolve it later. */
            break;
        case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
            bsdtar->option_dont_traverse_mounts = 1;
            break;
#if 0
        /*
         * The common BSD -P option is not necessary, since
         * our default is to archive symlinks, not follow
         * them.  This is convenient, as -P conflicts with GNU
         * tar anyway.
         */
        case 'P': /* BSD convention */
            /* Default behavior, no option necessary. */
            break;
#endif
        case 'P': /* GNU tar */
            bsdtar->extract_flags &= ~SECURITY;
            bsdtar->option_absolute_paths = 1;
            break;
        case 'p': /* GNU tar, star */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
            break;
        case OPTION_POSIX: /* GNU tar */
            bsdtar->create_format = "pax";
            break;
        case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
            bsdtar->option_fast_read = 1;
            break;
        case 'r': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'S': /* NetBSD pax-as-tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
            break;
        case 's': /* NetBSD pax-as-tar */
#if HAVE_REGEX_H
            add_substitution(bsdtar, bsdtar->optarg);
#else
            lafe_warnc(0,
                "-s is not supported by this version of bsdtar");
            usage();
#endif
            break;
        case OPTION_SAME_OWNER: /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
            break;
        case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
            bsdtar->strip_components = atoi(bsdtar->optarg);
            break;
        case 'T': /* GNU tar */
            bsdtar->names_from_file = bsdtar->optarg;
            break;
        case 't': /* SUSv2 */
            set_mode(bsdtar, opt);
            bsdtar->verbose++;
            break;
        case OPTION_TOTALS: /* GNU tar */
            bsdtar->option_totals++;
            break;
        case 'U': /* GNU tar */
            bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
            bsdtar->option_unlink_first = 1;
            break;
        case 'u': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'v': /* SUSv2 */
            bsdtar->verbose++;
            break;
        case OPTION_VERSION: /* GNU convention */
            version();
            break;
#if 0
        /*
         * The -W longopt feature is handled inside of
         * bsdtar_getopt(), so -W is not available here.
         */
        case 'W': /* Obscure GNU convention. */
            break;
#endif
        case 'w': /* SUSv2 */
            bsdtar->option_interactive = 1;
            break;
        case 'X': /* GNU tar */
            if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg))
                lafe_errc(1, 0,
                    "failed to process exclusions from file %s",
                    bsdtar->optarg);
            break;
        case 'x': /* SUSv2 */
            set_mode(bsdtar, opt);
            break;
        case 'y': /* FreeBSD version of GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'Z': /* GNU tar */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case 'z': /* GNU tar, star, many others */
            if (bsdtar->create_compression != '\0')
                lafe_errc(1, 0,
                    "Can't specify both -%c and -%c", opt,
                    bsdtar->create_compression);
            bsdtar->create_compression = opt;
            break;
        case OPTION_USE_COMPRESS_PROGRAM:
            bsdtar->compress_program = bsdtar->optarg;
            break;
        default:
            usage();
        }
    }

    /*
     * Sanity-check options.
     */

    /* If no "real" mode was specified, treat -h as --help. */
    if ((bsdtar->mode == '\0') && possible_help_request) {
        long_help();
        exit(0);
    }

    /* Otherwise, a mode is required. */
    if (bsdtar->mode == '\0')
        lafe_errc(1, 0,
            "Must specify one of -c, -r, -t, -u, -x");

    /* Check boolean options only permitted in certain modes. */
    if (bsdtar->option_dont_traverse_mounts)
        only_mode(bsdtar, "--one-file-system", "cru");
    if (bsdtar->option_fast_read)
        only_mode(bsdtar, "--fast-read", "xt");
    if (bsdtar->option_honor_nodump)
        only_mode(bsdtar, "--nodump", "cru");
    if (option_o > 0) {
        switch (bsdtar->mode) {
        case 'c':
            /*
             * In GNU tar, -o means "old format."  The
             * "ustar" format is the closest thing
             * supported by libarchive.
             */
            bsdtar->create_format = "ustar";
            /* TODO: bsdtar->create_format = "v7"; */
            break;
        case 'x':
            /* POSIX-compatible behavior. */
            bsdtar->option_no_owner = 1;
            bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
            break;
        default:
            only_mode(bsdtar, "-o", "xc");
            break;
        }
    }
    if (bsdtar->option_no_subdirs)
        only_mode(bsdtar, "-n", "cru");
    if (bsdtar->option_stdout)
        only_mode(bsdtar, "-O", "xt");
    if (bsdtar->option_unlink_first)
        only_mode(bsdtar, "-U", "x");
    if (bsdtar->option_warn_links)
        only_mode(bsdtar, "--check-links", "cr");

    /* Check other parameters only permitted in certain modes. */
    if (bsdtar->create_compression != '\0') {
        strcpy(buff, "-?");
        buff[1] = bsdtar->create_compression;
        only_mode(bsdtar, buff, "cxt");
    }
    if (bsdtar->create_format != NULL)
        only_mode(bsdtar, "--format", "cru");
    if (bsdtar->symlink_mode != '\0') {
        strcpy(buff, "-?");
        buff[1] = bsdtar->symlink_mode;
        only_mode(bsdtar, buff, "cru");
    }
    if (bsdtar->strip_components != 0)
        only_mode(bsdtar, "--strip-components", "xt");

    switch(bsdtar->mode) {
    case 'c':
        tar_mode_c(bsdtar);
        break;
    case 'r':
        tar_mode_r(bsdtar);
        break;
    case 't':
        tar_mode_t(bsdtar);
        break;
    case 'u':
        tar_mode_u(bsdtar);
        break;
    case 'x':
        tar_mode_x(bsdtar);
        break;
    }

    lafe_cleanup_exclusions(&bsdtar->matching);
#if HAVE_REGEX_H
    cleanup_substitution(bsdtar);
#endif

    if (bsdtar->return_value != 0)
        lafe_warnc(0,
            "Error exit delayed from previous errors.");
    return (bsdtar->return_value);
}
示例#30
0
int
main(int argc, char **argv)
{
	struct bsdtar		*bsdtar, bsdtar_storage;
	int			 opt, t;
	char			 compression, compression2;
	const char		*compression_name, *compression2_name;
	const char		*compress_program;
	char			 option_a, option_o;
	char			 possible_help_request;
	char			 buff[16];

	/*
	 * Use a pointer for consistency, but stack-allocated storage
	 * for ease of cleanup.
	 */
	bsdtar = &bsdtar_storage;
	memset(bsdtar, 0, sizeof(*bsdtar));
	bsdtar->fd = -1; /* Mark as "unused" */
	bsdtar->gid = -1;
	bsdtar->uid = -1;
	option_a = option_o = 0;
	compression = compression2 = '\0';
	compression_name = compression2_name = NULL;
	compress_program = NULL;

#if defined(HAVE_SIGACTION)
	{ /* Set up signal handling. */
		struct sigaction sa;
		sa.sa_handler = siginfo_handler;
		sigemptyset(&sa.sa_mask);
		sa.sa_flags = 0;
#ifdef SIGINFO
		if (sigaction(SIGINFO, &sa, NULL))
			lafe_errc(1, errno, "sigaction(SIGINFO) failed");
#endif
#ifdef SIGUSR1
		/* ... and treat SIGUSR1 the same way as SIGINFO. */
		if (sigaction(SIGUSR1, &sa, NULL))
			lafe_errc(1, errno, "sigaction(SIGUSR1) failed");
#endif
#ifdef SIGPIPE
		/* Ignore SIGPIPE signals. */
		sa.sa_handler = SIG_IGN;
		sigaction(SIGPIPE, &sa, NULL);
#endif
	}
#endif

	/* Set lafe_progname before calling lafe_warnc. */
	lafe_setprogname(*argv, "bsdtar");

#if HAVE_SETLOCALE
	if (setlocale(LC_ALL, "") == NULL)
		lafe_warnc(0, "Failed to set default locale");
#endif
#if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER)
	bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd');
#endif
	possible_help_request = 0;

	/* Look up uid of current user for future reference */
	bsdtar->user_uid = geteuid();

	/* Default: open tape drive. */
	bsdtar->filename = getenv("TAPE");
	if (bsdtar->filename == NULL)
		bsdtar->filename = _PATH_DEFTAPE;

	/* Default block size settings. */
	bsdtar->bytes_per_block = DEFAULT_BYTES_PER_BLOCK;
	/* Allow library to default this unless user specifies -b. */
	bsdtar->bytes_in_last_block = -1;

	/* Default: preserve mod time on extract */
	bsdtar->extract_flags = ARCHIVE_EXTRACT_TIME;

	/* Default: Perform basic security checks. */
	bsdtar->extract_flags |= SECURITY;

#ifndef _WIN32
	/* On POSIX systems, assume --same-owner and -p when run by
	 * the root user.  This doesn't make any sense on Windows. */
	if (bsdtar->user_uid == 0) {
		/* --same-owner */
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
		/* -p */
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
		bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
	}
#endif

	/*
	 * Enable Mac OS "copyfile()" extension by default.
	 * This has no effect on other platforms.
	 */
	bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
#ifdef COPYFILE_DISABLE_VAR
	if (getenv(COPYFILE_DISABLE_VAR))
		bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
#endif
	bsdtar->matching = archive_match_new();
	if (bsdtar->matching == NULL)
		lafe_errc(1, errno, "Out of memory");
	bsdtar->cset = cset_new();
	if (bsdtar->cset == NULL)
		lafe_errc(1, errno, "Out of memory");

	bsdtar->argv = argv;
	bsdtar->argc = argc;

	/*
	 * Comments following each option indicate where that option
	 * originated:  SUSv2, POSIX, GNU tar, star, etc.  If there's
	 * no such comment, then I don't know of anyone else who
	 * implements that option.
	 */
	while ((opt = bsdtar_getopt(bsdtar)) != -1) {
		switch (opt) {
		case 'a': /* GNU tar */
			option_a = 1; /* Record it and resolve it later. */
			break;
		case 'B': /* GNU tar */
			/* libarchive doesn't need this; just ignore it. */
			break;
		case 'b': /* SUSv2 */
			t = atoi(bsdtar->argument);
			if (t <= 0 || t > 8192)
				lafe_errc(1, 0,
				    "Argument to -b is out of range (1..8192)");
			bsdtar->bytes_per_block = 512 * t;
			/* Explicit -b forces last block size. */
			bsdtar->bytes_in_last_block = bsdtar->bytes_per_block;
			break;
		case OPTION_B64ENCODE:
			if (compression2 != '\0')
				lafe_errc(1, 0,
				    "Can't specify both --uuencode and "
				    "--b64encode");
			compression2 = opt;
			compression2_name = "b64encode";
			break;
		case 'C': /* GNU tar */
			if (strlen(bsdtar->argument) == 0)
				lafe_errc(1, 0,
				    "Meaningless option: -C ''");

			set_chdir(bsdtar, bsdtar->argument);
			break;
		case 'c': /* SUSv2 */
			set_mode(bsdtar, opt);
			break;
		case OPTION_CHECK_LINKS: /* GNU tar */
			bsdtar->option_warn_links = 1;
			break;
		case OPTION_CHROOT: /* NetBSD */
			bsdtar->option_chroot = 1;
			break;
		case OPTION_DISABLE_COPYFILE: /* Mac OS X */
			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
			break;
		case OPTION_EXCLUDE: /* GNU tar */
			if (archive_match_exclude_pattern(
			    bsdtar->matching, bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0,
				    "Couldn't exclude %s\n", bsdtar->argument);
			break;
		case OPTION_FORMAT: /* GNU tar, others */
			cset_set_format(bsdtar->cset, bsdtar->argument);
			break;
		case 'f': /* SUSv2 */
			bsdtar->filename = bsdtar->argument;
			break;
		case OPTION_GID: /* cpio */
			t = atoi(bsdtar->argument);
			if (t < 0)
				lafe_errc(1, 0,
				    "Argument to --gid must be positive");
			bsdtar->gid = t;
			break;
		case OPTION_GNAME: /* cpio */
			bsdtar->gname = bsdtar->argument;
			break;
		case OPTION_GRZIP:
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "grzip";
			break;
		case 'H': /* BSD convention */
			bsdtar->symlink_mode = 'H';
			break;
		case 'h': /* Linux Standards Base, gtar; synonym for -L */
			bsdtar->symlink_mode = 'L';
			/* Hack: -h by itself is the "help" command. */
			possible_help_request = 1;
			break;
		case OPTION_HELP: /* GNU tar, others */
			long_help();
			exit(0);
			break;
		case OPTION_HFS_COMPRESSION: /* Mac OS X v10.6 or later */
			bsdtar->extract_flags |=
			    ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED;
			break;
		case OPTION_IGNORE_ZEROS:
			bsdtar->option_ignore_zeros = 1;
			break;
		case 'I': /* GNU tar */
			/*
			 * TODO: Allow 'names' to come from an archive,
			 * not just a text file.  Design a good UI for
			 * allowing names and mode/owner to be read
			 * from an archive, with contents coming from
			 * disk.  This can be used to "refresh" an
			 * archive or to design archives with special
			 * permissions without having to create those
			 * permissions on disk.
			 */
			bsdtar->names_from_file = bsdtar->argument;
			break;
		case OPTION_INCLUDE:
			/*
			 * No one else has the @archive extension, so
			 * no one else needs this to filter entries
			 * when transforming archives.
			 */
			if (archive_match_include_pattern(bsdtar->matching,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0,
				    "Failed to add %s to inclusion list",
				    bsdtar->argument);
			break;
		case 'j': /* GNU tar */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "bzip2";
			break;
		case 'J': /* GNU tar 1.21 and later */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "xz";
			break;
		case 'k': /* GNU tar */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
			break;
		case OPTION_KEEP_NEWER_FILES: /* GNU tar */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
			break;
		case 'L': /* BSD convention */
			bsdtar->symlink_mode = 'L';
			break;
	        case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
			/* GNU tar 1.13  used -l for --one-file-system */
			bsdtar->option_warn_links = 1;
			break;
		case OPTION_LRZIP:
		case OPTION_LZ4:
		case OPTION_LZIP: /* GNU tar beginning with 1.23 */
		case OPTION_LZMA: /* GNU tar beginning with 1.20 */
		case OPTION_LZOP: /* GNU tar beginning with 1.21 */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			switch (opt) {
			case OPTION_LRZIP: compression_name = "lrzip"; break;
			case OPTION_LZ4:  compression_name = "lz4"; break;
			case OPTION_LZIP: compression_name = "lzip"; break; 
			case OPTION_LZMA: compression_name = "lzma"; break; 
			case OPTION_LZOP: compression_name = "lzop"; break; 
			}
			break;
		case 'm': /* SUSv2 */
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
			break;
		case 'n': /* GNU tar */
			bsdtar->option_no_subdirs = 1;
			break;
	        /*
		 * Selecting files by time:
		 *    --newer-?time='date' Only files newer than 'date'
		 *    --newer-?time-than='file' Only files newer than time
		 *         on specified file (useful for incremental backups)
		 */
		case OPTION_NEWER_CTIME: /* GNU tar */
			if (archive_match_include_date(bsdtar->matching,
			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_NEWER_CTIME_THAN:
			if (archive_match_include_file_time(bsdtar->matching,
			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_NEWER_MTIME: /* GNU tar */
			if (archive_match_include_date(bsdtar->matching,
			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_NEWER_MTIME_THAN:
			if (archive_match_include_file_time(bsdtar->matching,
			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_NODUMP: /* star */
			bsdtar->readdisk_flags |= ARCHIVE_READDISK_HONOR_NODUMP;
			break;
		case OPTION_NOPRESERVE_HFS_COMPRESSION:
			/* Mac OS X v10.6 or later */
			bsdtar->extract_flags |=
			    ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
			break;
		case OPTION_NO_SAME_OWNER: /* GNU tar */
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
			break;
		case OPTION_NO_SAME_PERMISSIONS: /* GNU tar */
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_PERM;
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
			break;
		case OPTION_NULL: /* GNU tar */
			bsdtar->option_null++;
			break;
		case OPTION_NUMERIC_OWNER: /* GNU tar */
			bsdtar->uname = "";
			bsdtar->gname = "";
			bsdtar->option_numeric_owner++;
			break;
		case 'O': /* GNU tar */
			bsdtar->option_stdout = 1;
			break;
		case 'o': /* SUSv2 and GNU conflict here, but not fatally */
			option_o = 1; /* Record it and resolve it later. */
			break;
	        /*
		 * Selecting files by time:
		 *    --older-?time='date' Only files older than 'date'
		 *    --older-?time-than='file' Only files older than time
		 *         on specified file
		 */
		case OPTION_OLDER_CTIME:
			if (archive_match_include_date(bsdtar->matching,
			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_OLDER_CTIME_THAN:
			if (archive_match_include_file_time(bsdtar->matching,
			    ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_OLDER_MTIME:
			if (archive_match_include_date(bsdtar->matching,
			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_OLDER_MTIME_THAN:
			if (archive_match_include_file_time(bsdtar->matching,
			    ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
			    bsdtar->argument) != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
			bsdtar->readdisk_flags |=
			    ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS;
			break;
		case OPTION_OPTIONS:
			bsdtar->option_options = bsdtar->argument;
			break;
#if 0
		/*
		 * The common BSD -P option is not necessary, since
		 * our default is to archive symlinks, not follow
		 * them.  This is convenient, as -P conflicts with GNU
		 * tar anyway.
		 */
		case 'P': /* BSD convention */
			/* Default behavior, no option necessary. */
			break;
#endif
		case 'P': /* GNU tar */
			bsdtar->extract_flags &= ~SECURITY;
			bsdtar->option_absolute_paths = 1;
			break;
		case 'p': /* GNU tar, star */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
			break;
		case OPTION_PASSPHRASE:
			bsdtar->passphrase = bsdtar->argument;
			break;
		case OPTION_POSIX: /* GNU tar */
			cset_set_format(bsdtar->cset, "pax");
			break;
		case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
			bsdtar->option_fast_read = 1;
			break;
		case 'r': /* SUSv2 */
			set_mode(bsdtar, opt);
			break;
		case 'S': /* NetBSD pax-as-tar */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_SPARSE;
			break;
		case 's': /* NetBSD pax-as-tar */
#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
			add_substitution(bsdtar, bsdtar->argument);
#else
			lafe_warnc(0,
			    "-s is not supported by this version of bsdtar");
			usage();
#endif
			break;
		case OPTION_SAME_OWNER: /* GNU tar */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
			break;
		case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */
			errno = 0;
			bsdtar->strip_components = strtol(bsdtar->argument,
			    NULL, 0);
			if (errno)
				lafe_errc(1, 0,
				    "Invalid --strip-components argument: %s",
				    bsdtar->argument);
			break;
		case 'T': /* GNU tar */
			bsdtar->names_from_file = bsdtar->argument;
			break;
		case 't': /* SUSv2 */
			set_mode(bsdtar, opt);
			bsdtar->verbose++;
			break;
		case OPTION_TOTALS: /* GNU tar */
			bsdtar->option_totals++;
			break;
		case 'U': /* GNU tar */
			bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
			bsdtar->option_unlink_first = 1;
			break;
		case 'u': /* SUSv2 */
			set_mode(bsdtar, opt);
			break;
		case OPTION_UID: /* cpio */
			t = atoi(bsdtar->argument);
			if (t < 0)
				lafe_errc(1, 0,
				    "Argument to --uid must be positive");
			bsdtar->uid = t;
			break;
		case OPTION_UNAME: /* cpio */
			bsdtar->uname = bsdtar->argument;
			break;
		case OPTION_UUENCODE:
			if (compression2 != '\0')
				lafe_errc(1, 0,
				    "Can't specify both --uuencode and "
				    "--b64encode");
			compression2 = opt;
			compression2_name = "uuencode";
			break;
		case 'v': /* SUSv2 */
			bsdtar->verbose++;
			break;
		case OPTION_VERSION: /* GNU convention */
			version();
			break;
#if 0
		/*
		 * The -W longopt feature is handled inside of
		 * bsdtar_getopt(), so -W is not available here.
		 */
		case 'W': /* Obscure GNU convention. */
			break;
#endif
		case 'w': /* SUSv2 */
			bsdtar->option_interactive = 1;
			break;
		case 'X': /* GNU tar */
			if (archive_match_exclude_pattern_from_file(
			    bsdtar->matching, bsdtar->argument, 0)
			    != ARCHIVE_OK)
				lafe_errc(1, 0, "Error : %s",
				    archive_error_string(bsdtar->matching));
			break;
		case 'x': /* SUSv2 */
			set_mode(bsdtar, opt);
			break;
		case 'y': /* FreeBSD version of GNU tar */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "bzip2";
			break;
		case 'Z': /* GNU tar */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "compress";
			break;
		case 'z': /* GNU tar, star, many others */
			if (compression != '\0')
				lafe_errc(1, 0,
				    "Can't specify both -%c and -%c", opt,
				    compression);
			compression = opt;
			compression_name = "gzip";
			break;
		case OPTION_USE_COMPRESS_PROGRAM:
			compress_program = bsdtar->argument;
			break;
		default:
			usage();
		}
	}

	/*
	 * Sanity-check options.
	 */

	/* If no "real" mode was specified, treat -h as --help. */
	if ((bsdtar->mode == '\0') && possible_help_request) {
		long_help();
		exit(0);
	}

	/* Otherwise, a mode is required. */
	if (bsdtar->mode == '\0')
		lafe_errc(1, 0,
		    "Must specify one of -c, -r, -t, -u, -x");

	/* Check boolean options only permitted in certain modes. */
	if (option_a)
		only_mode(bsdtar, "-a", "c");
	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
		only_mode(bsdtar, "--one-file-system", "cru");
	if (bsdtar->option_fast_read)
		only_mode(bsdtar, "--fast-read", "xt");
	if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED)
		only_mode(bsdtar, "--hfsCompression", "x");
	if (bsdtar->extract_flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION)
		only_mode(bsdtar, "--nopreserveHFSCompression", "x");
	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP)
		only_mode(bsdtar, "--nodump", "cru");
	if (option_o > 0) {
		switch (bsdtar->mode) {
		case 'c':
			/*
			 * In GNU tar, -o means "old format."  The
			 * "ustar" format is the closest thing
			 * supported by libarchive.
			 */
			cset_set_format(bsdtar->cset, "ustar");
			/* TODO: bsdtar->create_format = "v7"; */
			break;
		case 'x':
			/* POSIX-compatible behavior. */
			bsdtar->option_no_owner = 1;
			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
			break;
		default:
			only_mode(bsdtar, "-o", "xc");
			break;
		}
	}
	if (bsdtar->option_no_subdirs)
		only_mode(bsdtar, "-n", "cru");
	if (bsdtar->option_stdout)
		only_mode(bsdtar, "-O", "xt");
	if (bsdtar->option_unlink_first)
		only_mode(bsdtar, "-U", "x");
	if (bsdtar->option_warn_links)
		only_mode(bsdtar, "--check-links", "cr");

	if (option_a && cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
		/* Ignore specified compressions if auto-compress works. */
		compression = '\0';
		compression2 = '\0';
	}
	/* Check other parameters only permitted in certain modes. */
	if (compress_program != NULL) {
		only_mode(bsdtar, "--use-compress-program", "cxt");
		cset_add_filter_program(bsdtar->cset, compress_program);
		/* Ignore specified compressions. */
		compression = '\0';
		compression2 = '\0';
	}
	if (compression != '\0') {
		switch (compression) {
		case 'J': case 'j': case 'y': case 'Z': case 'z':
			strcpy(buff, "-?");
			buff[1] = compression;
			break;
		default:
			strcpy(buff, "--");
			strcat(buff, compression_name);
			break;
		}
		only_mode(bsdtar, buff, "cxt");
		cset_add_filter(bsdtar->cset, compression_name);
	}
	if (compression2 != '\0') {
		strcpy(buff, "--");
		strcat(buff, compression2_name);
		only_mode(bsdtar, buff, "cxt");
		cset_add_filter(bsdtar->cset, compression2_name);
	}
	if (cset_get_format(bsdtar->cset) != NULL)
		only_mode(bsdtar, "--format", "cru");
	if (bsdtar->symlink_mode != '\0') {
		strcpy(buff, "-?");
		buff[1] = bsdtar->symlink_mode;
		only_mode(bsdtar, buff, "cru");
	}

	/* Filename "-" implies stdio. */
	if (strcmp(bsdtar->filename, "-") == 0)
		bsdtar->filename = NULL;

	switch(bsdtar->mode) {
	case 'c':
		tar_mode_c(bsdtar);
		break;
	case 'r':
		tar_mode_r(bsdtar);
		break;
	case 't':
		tar_mode_t(bsdtar);
		break;
	case 'u':
		tar_mode_u(bsdtar);
		break;
	case 'x':
		tar_mode_x(bsdtar);
		break;
	}

	archive_match_free(bsdtar->matching);
#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
	cleanup_substitution(bsdtar);
#endif
	cset_free(bsdtar->cset);
	passphrase_free(bsdtar->ppbuff);

	if (bsdtar->return_value != 0)
		lafe_warnc(0,
		    "Error exit delayed from previous errors.");
	return (bsdtar->return_value);
}