From 2fa0c563681ad858e1e027961818a2828246e6e4 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Thu, 4 Dec 2008 17:08:00 -0500 Subject: [PATCH] IFC SVN-Revision: 264 --- cpio/cpio.c | 233 +++++++++++++++++++++++++++-- cpio/cpio.h | 6 +- cpio/test/test_option_tv.stdout.uu | 5 +- tar/config_freebsd.h | 3 +- tar/tree.c | 37 ++++- tar/tree.h | 36 +++-- tar/util.c | 169 ++++++++++++++------- tar/write.c | 13 +- 8 files changed, 412 insertions(+), 90 deletions(-) diff --git a/cpio/cpio.c b/cpio/cpio.c index 5d79f8e20..87932aa2c 100644 --- a/cpio/cpio.c +++ b/cpio/cpio.c @@ -26,7 +26,7 @@ #include "cpio_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.13 2008/09/04 05:20:46 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.14 2008/11/29 20:22:02 kientzle Exp $"); #include #include @@ -41,6 +41,12 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.13 2008/09/04 05:20:46 kientzle #ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif #ifdef HAVE_STDARG_H #include #endif @@ -58,11 +64,32 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.13 2008/09/04 05:20:46 kientzle #include "cpio.h" #include "matching.h" +/* Fixed size of uname/gname caches. */ +#define name_cache_size 101 + +struct name_cache { + int probes; + int hits; + size_t size; + struct { + id_t id; + char *name; + } cache[name_cache_size]; +}; + static int copy_data(struct archive *, struct archive *); static const char *cpio_rename(const char *name); static int entry_to_archive(struct cpio *, struct archive_entry *); static int file_to_archive(struct cpio *, const char *); +static void free_cache(struct name_cache *cache); +static void list_item_verbose(struct cpio *, struct archive_entry *); static void long_help(void); +static const char *lookup_gname(struct cpio *, gid_t gid); +static int lookup_gname_helper(struct cpio *, + const char **name, id_t gid); +static const char *lookup_uname(struct cpio *, uid_t uid); +static int lookup_uname_helper(struct cpio *, + const char **name, id_t uid); static void mode_in(struct cpio *); static void mode_list(struct cpio *); static void mode_out(struct cpio *); @@ -268,6 +295,8 @@ main(int argc, char *argv[]) "Must specify at least one of -i, -o, or -p"); } + free_cache(cpio->gname_cache); + free_cache(cpio->uname_cache); return (0); } @@ -798,18 +827,9 @@ mode_list(struct cpio *cpio) } if (excluded(cpio, archive_entry_pathname(entry))) continue; - if (cpio->verbose) { - /* TODO: uname/gname lookups */ - /* TODO: Clean this up. */ - fprintf(stdout, - "%s%3d %8s%8s " CPIO_FILESIZE_PRINTF " %s\n", - archive_entry_strmode(entry), - archive_entry_nlink(entry), - archive_entry_uname(entry), - archive_entry_gname(entry), - (CPIO_FILESIZE_TYPE)archive_entry_size(entry), - archive_entry_pathname(entry)); - } else + if (cpio->verbose) + list_item_verbose(cpio, entry); + else fprintf(stdout, "%s\n", archive_entry_pathname(entry)); } r = archive_read_close(a); @@ -825,6 +845,73 @@ mode_list(struct cpio *cpio) exit(0); } +/* + * 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'. + */ +static void +list_item_verbose(struct cpio *cpio, struct archive_entry *entry) +{ + char size[32]; + char date[32]; + const char *uname, *gname; + FILE *out = stdout; + const struct stat *st; + const char *fmt; + time_t tim; + static time_t now; + + st = archive_entry_stat(entry); + + if (!now) + time(&now); + + /* Use uname if it's present, else uid. */ + uname = archive_entry_uname(entry); + if (uname == NULL) + uname = lookup_uname(cpio, archive_entry_uid(entry)); + + /* Use gname if it's present, else gid. */ + gname = archive_entry_gname(entry); + if (gname == NULL) + gname = lookup_gname(cpio, archive_entry_gid(entry)); + + /* Print device number or file size. */ + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + snprintf(size, sizeof(size), "%lu,%lu", + (unsigned long)major(st->st_rdev), + (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ + } else { + snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF, + (CPIO_FILESIZE_TYPE)st->st_size); + } + + /* Format the time using 'ls -l' conventions. */ + tim = (time_t)st->st_mtime; + if (abs(tim - now) > (365/2)*86400) + fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; + else + fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; + strftime(date, sizeof(date), fmt, localtime(&tim)); + + fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", + archive_entry_strmode(entry), + archive_entry_nlink(entry), + uname, gname, size, date, + archive_entry_pathname(entry)); + + /* Extra information for links. */ + if (archive_entry_hardlink(entry)) /* Hard link */ + fprintf(out, " link to %s", archive_entry_hardlink(entry)); + else if (archive_entry_symlink(entry)) /* Symbolic link */ + fprintf(out, " -> %s", archive_entry_symlink(entry)); + fprintf(out, "\n"); +} + static void mode_pass(struct cpio *cpio, const char *destdir) { @@ -1033,3 +1120,123 @@ process_lines_free(struct line_reader *lr) free(lr->pathname); free(lr); } + +static void +free_cache(struct name_cache *cache) +{ + size_t i; + + if (cache != NULL) { + for (i = 0; i < cache->size; i++) + free(cache->cache[i].name); + free(cache); + } +} + +/* + * 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) + cpio_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); +} + +static const char * +lookup_uname(struct cpio *cpio, uid_t uid) +{ + return (lookup_name(cpio, &cpio->uname_cache, + &lookup_uname_helper, (id_t)uid)); +} + +static int +lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) +{ + struct passwd *pwent; + + (void)cpio; /* UNUSED */ + + errno = 0; + pwent = getpwuid((uid_t)id); + if (pwent == NULL) { + *name = NULL; + if (errno != 0) + cpio_warnc(errno, "getpwuid(%d) failed", id); + return (errno); + } + + *name = pwent->pw_name; + return (0); +} + +static const char * +lookup_gname(struct cpio *cpio, gid_t gid) +{ + return (lookup_name(cpio, &cpio->gname_cache, + &lookup_gname_helper, (id_t)gid)); +} + +static int +lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) +{ + struct group *grent; + + (void)cpio; /* UNUSED */ + + errno = 0; + grent = getgrgid((gid_t)id); + if (grent == NULL) { + *name = NULL; + if (errno != 0) + cpio_warnc(errno, "getgrgid(%d) failed", id); + return (errno); + } + + *name = grent->gr_name; + return (0); +} diff --git a/cpio/cpio.h b/cpio/cpio.h index 40b04cdde..665a937ca 100644 --- a/cpio/cpio.h +++ b/cpio/cpio.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/cpio/cpio.h,v 1.4 2008/08/04 01:25:48 cperciva Exp $ + * $FreeBSD: src/usr.bin/cpio/cpio.h,v 1.5 2008/11/29 20:22:02 kientzle Exp $ */ #ifndef CPIO_H_INCLUDED @@ -68,6 +68,7 @@ struct cpio { char *pass_destpath; int uid_override; int gid_override; + int day_first; /* true if locale prefers day/mon */ /* If >= 0, then close this when done. */ int fd; @@ -79,6 +80,9 @@ struct cpio { int return_value; /* Value returned by main() */ struct archive_entry_linkresolver *linkresolver; + struct name_cache *uname_cache; + struct name_cache *gname_cache; + /* Work data. */ struct matching *matching; char *buff; diff --git a/cpio/test/test_option_tv.stdout.uu b/cpio/test/test_option_tv.stdout.uu index baead3ee3..e28888ac4 100644 --- a/cpio/test/test_option_tv.stdout.uu +++ b/cpio/test/test_option_tv.stdout.uu @@ -1,5 +1,6 @@ -$FreeBSD$ +$FreeBSD: src/usr.bin/cpio/test/test_option_tv.stdout.uu,v 1.2 2008/11/29 20:22:02 kientzle Exp $ begin 644 test_option_tv.stdout -G+7)W+7(M+7(M+2`@(#$@("`H;G5L;"D@("AN=6QL*2`P(&9I;&4* +M+7)W+7(M+7(M+2`@(#$@=&EM("`@("`@=&EM("`@("`@("`@("`@(#`@1&5C +/(#,Q("`Q.38Y(&9I;&4* ` end diff --git a/tar/config_freebsd.h b/tar/config_freebsd.h index ee6f8a184..16f2b3956 100644 --- a/tar/config_freebsd.h +++ b/tar/config_freebsd.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.7 2008/11/08 04:43:24 kientzle Exp $ + * $FreeBSD: src/usr.bin/tar/config_freebsd.h,v 1.8 2008/11/29 20:06:53 kientzle Exp $ */ /* A default configuration for FreeBSD, used if there is no config.h. */ @@ -101,6 +101,7 @@ #define HAVE_UNISTD_H 1 #define HAVE_UNSIGNED_LONG_LONG #define HAVE_VPRINTF 1 +#define HAVE_WCTYPE_H 1 #define HAVE_ZLIB_H 1 #undef MAJOR_IN_MKDEV #define STDC_HEADERS 1 diff --git a/tar/tree.c b/tar/tree.c index 23e64e3da..2b076582b 100644 --- a/tar/tree.c +++ b/tar/tree.c @@ -43,7 +43,7 @@ * regular dir or via fchdir(2) for a symlink). */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.8 2007/03/11 10:36:42 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.9 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -227,20 +227,28 @@ tree_open(const char *path) /* * We've finished a directory; ascend back to the parent. */ -static void +static int tree_ascend(struct tree *t) { struct tree_entry *te; + int r = 0; te = t->stack; t->depth--; if (te->flags & isDirLink) { - fchdir(te->fd); + if (fchdir(te->fd) != 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; + } close(te->fd); t->openCount--; } else { - chdir(".."); + if (chdir("..") != 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; + } } + return (r); } /* @@ -272,6 +280,17 @@ int tree_next(struct tree *t) { struct dirent *de = NULL; + int r; + + /* If we're called again after a fatal error, that's an API + * violation. Just crash now. */ + if (t->visit_type == TREE_ERROR_FATAL) { + const char *msg = "Unable to continue traversing" + " directory heirarchy after a fatal error."; + write(2, msg, strlen(msg)); + *(int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */ + exit(1); /* In case the SEGV didn't work. */ + } /* Handle the startup case by returning the initial entry. */ if (t->flags & needsReturn) { @@ -327,10 +346,11 @@ tree_next(struct tree *t) t->depth++; t->d = opendir("."); if (t->d == NULL) { - tree_ascend(t); /* Undo "chdir" */ + r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; - return (t->visit_type = TREE_ERROR_DIR); + t->visit_type = r != 0 ? r : TREE_ERROR_DIR; + return (t->visit_type); } t->flags &= ~hasLstat; t->flags &= ~hasStat; @@ -340,11 +360,12 @@ tree_next(struct tree *t) /* We've done everything necessary for the top stack entry. */ if (t->stack->flags & needsPostVisit) { - tree_ascend(t); + r = tree_ascend(t); tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; - return (t->visit_type = TREE_POSTASCENT); + t->visit_type = r != 0 ? r : TREE_POSTASCENT; + return (t->visit_type); } } return (t->visit_type = 0); diff --git a/tar/tree.h b/tar/tree.h index a32a15f8f..30d96fe3b 100644 --- a/tar/tree.h +++ b/tar/tree.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/tar/tree.h,v 1.3 2007/01/09 08:12:17 kientzle Exp $ + * $FreeBSD: src/usr.bin/tar/tree.h,v 1.4 2008/11/27 05:49:52 kientzle Exp $ */ /*- @@ -36,8 +36,7 @@ * * Supports very deep logical traversals. The fts package * uses "non-chdir" approach for logical traversals. This * package does use a chdir approach for logical traversals - * and can therefore handle pathnames much longer than - * PATH_MAX. + * and can therefore handle pathnames much longer than PATH_MAX. * * Supports deep physical traversals "out of the box." * Due to the memory optimizations above, there's no need to * limit dir names to 32k. @@ -53,23 +52,31 @@ struct tree *tree_open(const char * /* pathname */); void tree_close(struct tree *); /* - * tree_next() returns Zero if there is no next entry, non-zero if there is. - * Note that directories are potentially visited three times. The first - * time as "regular" file. If tree_descend() is invoked at that time, - * the directory is added to a work list and will be visited two more - * times: once just after descending into the directory and again - * just after ascending back to the parent. + * tree_next() returns Zero if there is no next entry, non-zero if + * there is. Note that directories are potentially visited three + * times. Directories are always visited first as part of enumerating + * their parent. If tree_descend() is invoked at that time, the + * directory is added to a work list and will subsequently be visited + * two more times: once just after descending into the directory and + * again just after ascending back to the parent. * - * TREE_ERROR is returned if the descent failed (because the + * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned - * instead of TREE_PREVISIT/TREE_POSTVISIT. + * instead of TREE_PREVISIT/TREE_POSTVISIT. TREE_ERROR_DIR is not a + * fatal error, but it does imply that the relevant subtree won't be + * visited. TREE_ERROR_FATAL is returned for an error that left the + * traversal completely hosed. Right now, this is only returned for + * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 +#define TREE_ERROR_FATAL -2 + int tree_next(struct tree *); +/* Errno value associated with the last traversal error. */ int tree_errno(struct tree *); /* @@ -85,7 +92,9 @@ void tree_descend(struct tree *); * Return information about the current entry. */ +/* Current depth in the traversal. */ int tree_current_depth(struct tree *); + /* * The current full pathname, length of the full pathname, * and a name that can be used to access the file. @@ -95,6 +104,7 @@ int tree_current_depth(struct tree *); const char *tree_current_path(struct tree *); size_t tree_current_pathlen(struct tree *); const char *tree_current_access_path(struct tree *); + /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the @@ -103,7 +113,9 @@ const char *tree_current_access_path(struct tree *); */ const struct stat *tree_current_stat(struct tree *); const struct stat *tree_current_lstat(struct tree *); -/* The following tests may use mechanisms much faster than stat()/lstat(). */ + +/* The following functions use tricks to avoid a certain number of + * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ int tree_current_is_physical_dir(struct tree *); /* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */ diff --git a/tar/util.c b/tar/util.c index d357fabcb..2a93419dc 100644 --- a/tar/util.c +++ b/tar/util.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.21 2008/11/10 05:04:55 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.22 2008/11/29 20:06:53 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -46,92 +46,161 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.21 2008/11/10 05:04:55 kientzle E #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_WCTYPE_H +#include +#else +/* If we don't have wctype, we need to hack up some version of iswprint(). */ +#define iswprint isprint +#endif #include "bsdtar.h" static void bsdtar_vwarnc(struct bsdtar *, int code, const char *fmt, va_list ap); +static size_t bsdtar_expand_char(char *, size_t, char); static const char *strip_components(const char *path, int elements); +/* TODO: Hack up a version of mbtowc for platforms with no wide + * character support at all. I think the following might suffice, + * but it needs careful testing. + * #if !HAVE_MBTOWC + * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) + * #endif + */ + /* * Print a string, taking care with any non-printable characters. + * + * Note that we use a stack-allocated buffer to receive the formatted + * string if we can. This is partly performance (avoiding a call to + * malloc()), partly out of expedience (we have to call vsnprintf() + * before malloc() anyway to find out how big a buffer we need; we may + * as well point that first call at a small local buffer in case it + * works), but mostly for safety (so we can use this to print messages + * about out-of-memory conditions). */ void safe_fprintf(FILE *f, const char *fmt, ...) { - char *buff; - char *buff_heap; - int buff_length; + char fmtbuff_stack[256]; /* Place to format the printf() string. */ + char outbuff[256]; /* Buffer for outgoing characters. */ + char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ + char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ + int fmtbuff_length; int length; va_list ap; - char *p; + const char *p; unsigned i; - char buff_stack[256]; - char copy_buff[256]; + wchar_t wc; + char try_wc; /* Use a stack-allocated buffer if we can, for speed and safety. */ - buff_heap = NULL; - buff_length = sizeof(buff_stack); - buff = buff_stack; + fmtbuff_heap = NULL; + fmtbuff_length = sizeof(fmtbuff_stack); + fmtbuff = fmtbuff_stack; + /* Try formatting into the stack buffer. */ va_start(ap, fmt); - length = vsnprintf(buff, buff_length, fmt, ap); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); - /* If the result is too large, allocate a buffer on the heap. */ - if (length >= buff_length) { - buff_length = length+1; - buff_heap = malloc(buff_length); - /* Failsafe: use the truncated string if malloc fails. */ - if (buff_heap != NULL) { - buff = buff_heap; + + /* If the result was too large, allocate a buffer on the heap. */ + if (length >= fmtbuff_length) { + fmtbuff_length = length+1; + fmtbuff_heap = malloc(fmtbuff_length); + + /* Reformat the result into the heap buffer if we can. */ + if (fmtbuff_heap != NULL) { + fmtbuff = fmtbuff_heap; va_start(ap, fmt); - length = vsnprintf(buff, buff_length, fmt, ap); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); + } else { + /* Leave fmtbuff pointing to the truncated + * string in fmtbuff_stack. */ + length = sizeof(fmtbuff_stack) - 1; } } + /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit + * more portable, so we use that here instead. */ + mbtowc(NULL, NULL, 0); /* Reset the shift state. */ + /* Write data, expanding unprintable characters. */ - p = buff; + p = fmtbuff; i = 0; + try_wc = 1; while (*p != '\0') { - unsigned char c = *p++; - - if (isprint(c) && c != '\\') - copy_buff[i++] = c; - else { - copy_buff[i++] = '\\'; - switch (c) { - case '\a': copy_buff[i++] = 'a'; break; - case '\b': copy_buff[i++] = 'b'; break; - case '\f': copy_buff[i++] = 'f'; break; - case '\n': copy_buff[i++] = 'n'; break; -#if '\r' != '\n' - /* On some platforms, \n and \r are the same. */ - case '\r': copy_buff[i++] = 'r'; break; -#endif - case '\t': copy_buff[i++] = 't'; break; - case '\v': copy_buff[i++] = 'v'; break; - case '\\': copy_buff[i++] = '\\'; break; - default: - sprintf(copy_buff + i, "%03o", c); - i += 3; + int n; + + /* Convert to wide char, test if the wide + * char is printable in the current locale. */ + if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { + length -= n; + if (iswprint(wc) && wc != L'\\') { + /* Printable, copy the bytes through. */ + while (n-- > 0) + outbuff[i++] = *p++; + } else { + /* Not printable, format the bytes. */ + while (n-- > 0) + i += bsdtar_expand_char( + outbuff, i, *p++); } + } else { + /* After any conversion failure, don't bother + * trying to convert the rest. */ + i += bsdtar_expand_char(outbuff, i, *p++); + try_wc = 0; } - /* If our temp buffer is full, dump it and keep going. */ - if (i > (sizeof(copy_buff) - 8)) { - copy_buff[i++] = '\0'; - fprintf(f, "%s", copy_buff); + /* If our output buffer is full, dump it and keep going. */ + if (i > (sizeof(outbuff) - 20)) { + outbuff[i++] = '\0'; + fprintf(f, "%s", outbuff); i = 0; } } - copy_buff[i++] = '\0'; - fprintf(f, "%s", copy_buff); + outbuff[i++] = '\0'; + fprintf(f, "%s", outbuff); + + /* If we allocated a heap-based formatting buffer, free it now. */ + if (fmtbuff_heap != NULL) + free(fmtbuff_heap); +} + +/* + * Render an arbitrary sequence of bytes into printable ASCII characters. + */ +static size_t +bsdtar_expand_char(char *buff, size_t offset, char c) +{ + size_t i = offset; + + if (isprint(c) && c != '\\') + buff[i++] = c; + else { + buff[i++] = '\\'; + switch (c) { + case '\a': buff[i++] = 'a'; break; + case '\b': buff[i++] = 'b'; break; + case '\f': buff[i++] = 'f'; break; + case '\n': buff[i++] = 'n'; break; +#if '\r' != '\n' + /* On some platforms, \n and \r are the same. */ + case '\r': buff[i++] = 'r'; break; +#endif + case '\t': buff[i++] = 't'; break; + case '\v': buff[i++] = 'v'; break; + case '\\': buff[i++] = '\\'; break; + default: + sprintf(buff + i, "%03o", 0xFF & (int)c); + i += 3; + } + } - /* If we allocated a heap-based buffer, free it now. */ - if (buff_heap != NULL) - free(buff_heap); + return (i - offset); } static void diff --git a/tar/write.c b/tar/write.c index fca625688..4d36018af 100644 --- a/tar/write.c +++ b/tar/write.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.77 2008/09/14 03:49:00 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -655,8 +655,15 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) const struct stat *st = NULL, *lst = NULL; int descend; - if (tree_ret == TREE_ERROR_DIR) - bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name); + if (tree_ret == TREE_ERROR_FATAL) + bsdtar_errc(bsdtar, 1, tree_errno(tree), + "%s: Unable to continue traversing directory tree", + name); + if (tree_ret == TREE_ERROR_DIR) { + bsdtar_warnc(bsdtar, errno, + "%s: Couldn't visit directory", name); + bsdtar->return_value = 1; + } if (tree_ret != TREE_REGULAR) continue; lst = tree_current_lstat(tree); -- 2.47.3