1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-06 13:09:50 +00:00

Support path-rewriting options (including --strip-components) for both

extraction and creation.  While I'm here, fix a bug reported by Garrett
Wollman: when stripping the leading '/' from the path "/", don't produce
an entry with an empty name; produce "." instead.
This commit is contained in:
Tim Kientzle 2005-04-17 17:20:54 +00:00
parent fe862a9b36
commit e90e8e4b97
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=145199
6 changed files with 115 additions and 90 deletions

View File

@ -6,7 +6,7 @@
#
PROG= bsdtar
VERSION= 1.01.022
VERSION= 1.01.023
DIST_SRCS= bsdtar.c getdate.y matching.c read.c util.c write.c
SRCS= ${DIST_SRCS}
WARNS?= 5

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003-2004 Tim Kientzle
* Copyright (c) 2003-2005 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -690,7 +690,7 @@ version(void)
{
printf("bsdtar %s, ", PACKAGE_VERSION);
printf("%s\n", archive_version());
printf("Copyright (C) 2003-2004 Tim Kientzle\n");
printf("Copyright (C) 2003-2005 Tim Kientzle\n");
exit(1);
}

View File

@ -106,6 +106,7 @@ void bsdtar_strmode(struct archive_entry *entry, char *bp);
void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...);
void cleanup_exclusions(struct bsdtar *);
void do_chdir(struct bsdtar *);
int edit_pathname(struct bsdtar *, struct archive_entry *);
int exclude(struct bsdtar *, const char *pattern);
int exclude_from_file(struct bsdtar *, const char *pathname);
int excluded(struct bsdtar *, const char *pathname);

View File

@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$");
#include "bsdtar.h"
static void cleanup_security(struct bsdtar *);
static int edit_pathname(struct bsdtar *, struct archive_entry *);
static void list_item_verbose(struct bsdtar *, FILE *,
struct archive_entry *);
static void read_archive(struct bsdtar *bsdtar, char mode);
@ -73,6 +72,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
FILE *out;
struct archive *a;
struct archive_entry *entry;
const struct stat *st;
int r;
while (*bsdtar->argv) {
@ -117,16 +117,37 @@ read_archive(struct bsdtar *bsdtar, char mode)
}
/*
* Note that exclusions are checked before pathname
* rewrites are handled. This gives more control over
* exclusions, since rewrites always lose information.
* (For example, consider a rewrite s/foo[0-9]/foo/.
* If we check exclusions after the rewrite, there
* would be no way to exclude foo1/bar while allowing
* foo2/bar.)
* Exclude entries that are too old.
*/
st = archive_entry_stat(entry);
if (bsdtar->newer_ctime_sec > 0) {
if (st->st_ctime < bsdtar->newer_ctime_sec)
continue; /* Too old, skip it. */
if (st->st_ctime == bsdtar->newer_ctime_sec
&& ARCHIVE_STAT_CTIME_NANOS(st)
<= bsdtar->newer_ctime_nsec)
continue; /* Too old, skip it. */
}
if (bsdtar->newer_mtime_sec > 0) {
if (st->st_mtime < bsdtar->newer_mtime_sec)
continue; /* Too old, skip it. */
if (st->st_mtime == bsdtar->newer_mtime_sec
&& ARCHIVE_STAT_MTIME_NANOS(st)
<= bsdtar->newer_mtime_nsec)
continue; /* Too old, skip it. */
}
/*
* Note that pattern exclusions are checked before
* pathname rewrites are handled. This gives more
* control over exclusions, since rewrites always lose
* information. (For example, consider a rewrite
* s/foo[0-9]/foo/. If we check exclusions after the
* rewrite, there would be no way to exclude foo1/bar
* while allowing foo2/bar.)
*/
if (excluded(bsdtar, archive_entry_pathname(entry)))
continue;
continue; /* Excluded by a pattern test. */
/*
* Modify the pathname as requested by the user. We
@ -137,7 +158,7 @@ read_archive(struct bsdtar *bsdtar, char mode)
* failures prevent extraction.
*/
if (edit_pathname(bsdtar, entry))
continue;
continue; /* Excluded by a rewrite failure. */
if (mode == 't') {
/* Perversely, gtar uses -O to mean "send to stderr"
@ -169,11 +190,17 @@ read_archive(struct bsdtar *bsdtar, char mode)
}
fprintf(out, "\n");
} else {
if (bsdtar->option_interactive &&
!yes("extract '%s'", archive_entry_pathname(entry)))
/*
* Skip security problems before prompting.
* Otherwise, the user may be confused that a
* file they wanted to extract was
* subsequently skipped.
*/
if (security_problem(bsdtar, entry))
continue;
if (security_problem(bsdtar, entry))
if (bsdtar->option_interactive &&
!yes("extract '%s'", archive_entry_pathname(entry)))
continue;
/*
@ -316,40 +343,6 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
}
/*
* Handle --strip-components and any future path-rewriting options.
* Returns non-zero if the pathname should not be extracted.
*/
static int
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
/* Strip leading dir names as per --strip-components option. */
if (bsdtar->strip_components > 0) {
int r = bsdtar->strip_components;
const char *name = archive_entry_pathname(entry);
const char *p = name;
char *q;
while (r > 0) {
switch (*p++) {
case '/':
r--;
name = p;
break;
case '\0':
/* Path is too short, skip it. */
return (1);
}
}
/* Safely replace name in archive_entry. */
q = strdup(name);
archive_entry_copy_pathname(entry, q);
free(q);
}
return (0);
}
/*
* Structure for storing path of last successful security check.
*/
@ -371,23 +364,11 @@ security_problem(struct bsdtar *bsdtar, struct archive_entry *entry)
char *p;
int r;
/* -P option forces us to just accept all pathnames. */
/* -P option forces us to just accept all pathnames as-is. */
if (bsdtar->option_absolute_paths)
return (0);
/* Strip leading '/'. */
name = archive_entry_pathname(entry);
if (name[0] == '/') {
/* Generate a warning the first time this happens. */
if (!bsdtar->warned_lead_slash) {
bsdtar_warnc(bsdtar, 0,
"Removing leading '/' from member names");
bsdtar->warned_lead_slash = 1;
}
while (name[0] == '/')
name++;
archive_entry_set_pathname(entry, name);
}
/* Reject any archive entry with '..' as a path element. */
pn = name;

View File

@ -380,3 +380,56 @@ do_chdir(struct bsdtar *bsdtar)
free(bsdtar->pending_chdir);
bsdtar->pending_chdir = NULL;
}
/*
* Handle --strip-components and any future path-rewriting options.
* Returns non-zero if the pathname should not be extracted.
*/
int
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
const char *name = archive_entry_pathname(entry);
/* Strip leading dir names as per --strip-components option. */
if (bsdtar->strip_components > 0) {
int r = bsdtar->strip_components;
const char *p = name;
while (r > 0) {
switch (*p++) {
case '/':
r--;
name = p;
break;
case '\0':
/* Path is too short, skip it. */
return (1);
}
}
}
/* Strip redundant "./" from start of filename. */
if (name[0] == '.' && name[1] == '/' && name[2] != '\0')
name += 2;
/* Strip leading '/' unless user has asked us not to. */
if (name[0] == '/' && !bsdtar->option_absolute_paths) {
/* Generate a warning the first time this happens. */
if (!bsdtar->warned_lead_slash) {
bsdtar_warnc(bsdtar, 0,
"Removing leading '/' from member names");
bsdtar->warned_lead_slash = 1;
}
name++;
if (*name == '\0') /* Strip '/' from "/" yields "." */
name = ".";
}
/* Safely replace name in archive_entry. */
if (name != archive_entry_pathname(entry)) {
char *q = strdup(name);
archive_entry_copy_pathname(entry, q);
free(q);
}
return (0);
}

View File

@ -126,7 +126,7 @@ static void write_entry(struct bsdtar *, struct archive *,
unsigned pathlen, const char *accpath);
static int write_file_data(struct bsdtar *, struct archive *,
int fd);
static void write_heirarchy(struct bsdtar *, struct archive *,
static void write_hierarchy(struct bsdtar *, struct archive *,
const char *);
void
@ -393,7 +393,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
if (append_archive(bsdtar, a, arg + 1) != 0)
break;
} else
write_heirarchy(bsdtar, a, arg);
write_hierarchy(bsdtar, a, arg);
}
bsdtar->argv++;
}
@ -434,7 +434,7 @@ archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line)
else {
if (*line != '/')
do_chdir(bsdtar); /* Handle a deferred -C */
write_heirarchy(bsdtar, bsdtar->archive, line);
write_hierarchy(bsdtar, bsdtar->archive, line);
}
return (0);
}
@ -509,10 +509,10 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, const char *filename)
}
/*
* Add the file or dir heirarchy named by 'path' to the archive
* Add the file or dir hierarchy named by 'path' to the archive
*/
static void
write_heirarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
{
FTS *fts;
FTSENT *ftsent;
@ -701,7 +701,7 @@ write_heirarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
break;
default:
bsdtar_warnc(bsdtar, 0,
"%s: Heirarchy traversal error %d\n",
"%s: Hierarchy traversal error %d\n",
ftsent->fts_path,
ftsent->fts_info);
break;
@ -736,26 +736,15 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
fd = -1;
entry = archive_entry_new();
/* Strip redundant "./" from start of filename. */
if (pathname != NULL && pathname[0] == '.' && pathname[1] == '/') {
pathname += 2;
if (*pathname == '\0') /* This is the "./" directory. */
goto cleanup; /* Don't archive it ever. */
}
/* Strip leading '/' unless user has asked us not to. */
if (pathname && pathname[0] == '/' && !bsdtar->option_absolute_paths) {
/* Generate a warning the first time this happens. */
if (!bsdtar->warned_lead_slash) {
bsdtar_warnc(bsdtar, 0,
"Removing leading '/' from member names");
bsdtar->warned_lead_slash = 1;
}
pathname++;
}
archive_entry_set_pathname(entry, pathname);
/*
* Rewrite the pathname to be archived. If rewrite
* fails, skip the entry.
*/
if (edit_pathname(bsdtar, entry))
goto abort;
if (!S_ISDIR(st->st_mode) && (st->st_nlink > 1))
lookup_hardlink(bsdtar, entry, st);
@ -847,14 +836,15 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
write_file_data(bsdtar, a, fd);
cleanup:
if (bsdtar->verbose)
fprintf(stderr, "\n");
abort:
if (fd >= 0)
close(fd);
if (entry != NULL)
archive_entry_free(entry);
if (bsdtar->verbose)
fprintf(stderr, "\n");
}