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:
parent
fe862a9b36
commit
e90e8e4b97
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=145199
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user