From ec0a4e2bdb95f66a113cf857064d8465ec7fa7d5 Mon Sep 17 00:00:00 2001 From: kientzle Date: Sun, 17 Apr 2005 17:20:54 +0000 Subject: 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. --- usr.bin/tar/Makefile | 2 +- usr.bin/tar/bsdtar.c | 4 +- usr.bin/tar/bsdtar.h | 1 + usr.bin/tar/read.c | 101 +++++++++++++++++++++------------------------------ usr.bin/tar/util.c | 53 +++++++++++++++++++++++++++ usr.bin/tar/write.c | 44 +++++++++------------- 6 files changed, 115 insertions(+), 90 deletions(-) (limited to 'usr.bin/tar') diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile index 320fe7d..1e5c25e 100644 --- a/usr.bin/tar/Makefile +++ b/usr.bin/tar/Makefile @@ -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 diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c index f2ecf03..c2ea3c5 100644 --- a/usr.bin/tar/bsdtar.c +++ b/usr.bin/tar/bsdtar.c @@ -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); } diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h index 12e210d..5c37a71 100644 --- a/usr.bin/tar/bsdtar.h +++ b/usr.bin/tar/bsdtar.h @@ -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); diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c index 1bb657e..dc95409 100644 --- a/usr.bin/tar/read.c +++ b/usr.bin/tar/read.c @@ -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; /* @@ -317,40 +344,6 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *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. */ struct security { @@ -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; diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c index 5071fe9..7f1a944 100644 --- a/usr.bin/tar/util.c +++ b/usr.bin/tar/util.c @@ -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); +} diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index aace228..9d430aa 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -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"); } -- cgit v1.1