diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index 7ea63fc7342d..d0fc2385c7f4 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -369,11 +369,10 @@ archive_entry_mtime(struct archive_entry *entry) return (entry->ae_stat.st_mtime); } - long archive_entry_mtime_nsec(struct archive_entry *entry) { - return (entry->ae_stat.st_mtimespec.tv_nsec); + return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat)); } const char * diff --git a/lib/libarchive/archive_platform.h b/lib/libarchive/archive_platform.h index 8a5552b80edf..e2642dc890df 100644 --- a/lib/libarchive/archive_platform.h +++ b/lib/libarchive/archive_platform.h @@ -29,7 +29,9 @@ /* * This header is the first thing included in any of the libarchive * source files. As far as possible, platform-specific issues should - * be dealt with here and not within individual source files. + * be dealt with here and not within individual source files. I'm + * actively trying to minimize #if blocks within the main source, + * since they obfuscate the code. */ #ifndef ARCHIVE_PLATFORM_H_INCLUDED @@ -57,10 +59,19 @@ #define ARCHIVE_ERRNO_PROGRAMMER EINVAL #define ARCHIVE_ERRNO_MISC (-1) +/* Fetch/set high-resolution time data through a struct stat pointer. */ +#define ARCHIVE_STAT_ATIME_NANOS(st) (st)->st_atimespec.tv_nsec +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n) + /* * Older versions of inttypes.h don't have INT64_MAX, etc. Since * SUSv3 requires them to be macros when they are defined, we can - * easily test for and define them here if necessary. + * easily test for and define them here if necessary. Someday, we + * won't have to worry about non-C99-compliant systems. */ #ifndef INT64_MAX /* XXX Is this really necessary? XXX */ @@ -82,16 +93,32 @@ /* Linux */ #ifdef LINUX -#define _FILE_OFFSET_BITS 64 +#define _FILE_OFFSET_BITS 64 /* Needed for 64-bit file size handling. */ #include #define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ #define ARCHIVE_ERRNO_PROGRAMMER EINVAL #define ARCHIVE_ERRNO_MISC (-1) -#define st_atimespec st_atim -#define st_mtimespec st_mtim -#define st_ctimespec st_ctim #define HAVE_STRERROR_R 1 #define STRERROR_R_CHAR_P 1 + +#ifdef HAVE_STRUCT_STAT_TIMESPEC +/* Fetch the nanosecond portion of the timestamp from a struct stat pointer. */ +#define ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec +#define ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec +#define ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n) +#else +/* High-res timestamps aren't available, so just use stubs here. */ +#define ARCHIVE_STAT_ATIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_CTIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_MTIME_NANOS(pstat) 0 +#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) +#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) +#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) +#endif + #endif /* diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index c402fb4d09b1..30dd5bcb02f8 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -116,37 +116,35 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags) { mode_t writable_mode; struct archive_extract_dir_entry *le; + const struct stat *st; int ret; int restore_pwd; restore_pwd = -1; - if (S_ISDIR(archive_entry_stat(entry)->st_mode)) { + st = archive_entry_stat(entry); + if (S_ISDIR(st->st_mode)) { /* * TODO: Does this really work under all conditions? * * E.g., root restores a dir owned by someone else? */ - writable_mode = archive_entry_stat(entry)->st_mode | 0700; + writable_mode = st->st_mode | 0700; /* * In order to correctly restore non-writable dirs or * dir timestamps, we need to maintain a fix-up list. */ - if (archive_entry_stat(entry)->st_mode != writable_mode || + if (st->st_mode != writable_mode || flags & ARCHIVE_EXTRACT_TIME) { le = malloc(sizeof(struct archive_extract_dir_entry)); le->next = a->archive_extract_dir_list; a->archive_extract_dir_list = le; - le->mode = archive_entry_stat(entry)->st_mode; - le->mtime = archive_entry_stat(entry)->st_mtime; - le->mtime_nanos = - archive_entry_stat(entry)->st_mtimespec.tv_nsec; - le->atime = archive_entry_stat(entry)->st_atime; - le->atime_nanos = - archive_entry_stat(entry)->st_atimespec.tv_nsec; - le->name = - malloc(strlen(archive_entry_pathname(entry)) + 1); - strcpy(le->name, archive_entry_pathname(entry)); + le->mode = st->st_mode; + le->mtime = st->st_mtime; + le->mtime_nanos = ARCHIVE_STAT_MTIME_NANOS(st); + le->atime = st->st_atime; + le->atime_nanos = ARCHIVE_STAT_ATIME_NANOS(st); + le->name = strdup(archive_entry_pathname(entry)); a->cleanup_archive_extract = archive_extract_cleanup; /* Make sure I can write to this directory. */ archive_entry_set_mode(entry, writable_mode); @@ -167,7 +165,7 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags) /* XXX Update pathname in 'entry' XXX */ } - switch (archive_entry_stat(entry)->st_mode & S_IFMT) { + switch (st->st_mode & S_IFMT) { default: /* Fall through, as required by POSIX. */ case S_IFREG: @@ -720,10 +718,10 @@ set_time(struct archive *a, struct archive_entry *entry, int flags) return (ARCHIVE_OK); times[1].tv_sec = st->st_mtime; - times[1].tv_usec = st->st_mtimespec.tv_nsec / 1000; + times[1].tv_usec = ARCHIVE_STAT_MTIME_NANOS(st) / 1000; times[0].tv_sec = st->st_atime; - times[0].tv_usec = st->st_atimespec.tv_nsec / 1000; + times[0].tv_usec = ARCHIVE_STAT_ATIME_NANOS(st) / 1000; #ifdef HAVE_LUTIMES if (lutimes(archive_entry_pathname(entry), times) != 0) { diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index eca8311247f8..e2f62424aa60 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -109,7 +109,7 @@ static int pax_attribute(struct archive_entry *, struct stat *, wchar_t *key, wchar_t *value); static int pax_header(struct archive *, struct tar *, struct archive_entry *, struct stat *, char *attr); -static void pax_time(const wchar_t *, struct timespec *t); +static void pax_time(const wchar_t *, int64_t *sec, long *nanos); static int read_body_to_string(struct archive *, struct archive_string *, const void *h); static int64_t tar_atol(const char *, unsigned); @@ -884,6 +884,9 @@ static int pax_attribute(struct archive_entry *entry, struct stat *st, wchar_t *key, wchar_t *value) { + int64_t s; + long n; + switch (key[0]) { case 'L': /* Our extensions */ @@ -913,13 +916,18 @@ pax_attribute(struct archive_entry *entry, struct stat *st, st->st_nlink = tar_atol10(value, wcslen(value)); break; case 'a': - if (wcscmp(key, L"atime")==0) - pax_time(value, &(st->st_atimespec)); + if (wcscmp(key, L"atime")==0) { + pax_time(value, &s, &n); + st->st_atime = s; + ARCHIVE_STAT_SET_ATIME_NANOS(st, n); + } break; case 'c': - if (wcscmp(key, L"ctime")==0) - pax_time(value, &(st->st_ctimespec)); - else if (wcscmp(key, L"charset")==0) { + if (wcscmp(key, L"ctime")==0) { + pax_time(value, &s, &n); + st->st_ctime = s; + ARCHIVE_STAT_SET_CTIME_NANOS(st, n); + } else if (wcscmp(key, L"charset")==0) { /* TODO: Publish charset information in entry. */ } else if (wcscmp(key, L"comment")==0) { /* TODO: Publish comment in entry. */ @@ -941,8 +949,11 @@ pax_attribute(struct archive_entry *entry, struct stat *st, } break; case 'm': - if (wcscmp(key, L"mtime")==0) - pax_time(value, &(st->st_mtimespec)); + if (wcscmp(key, L"mtime")==0) { + pax_time(value, &s, &n); + st->st_mtime = s; + ARCHIVE_STAT_SET_MTIME_NANOS(st, n); + } break; case 'p': if (wcscmp(key, L"path")==0) @@ -973,7 +984,7 @@ pax_attribute(struct archive_entry *entry, struct stat *st, * parse a decimal time value, which may include a fractional portion */ static void -pax_time(const wchar_t *p, struct timespec *t) +pax_time(const wchar_t *p, int64_t *ps, long *pn) { char digit; int64_t s; @@ -1000,10 +1011,10 @@ pax_time(const wchar_t *p, struct timespec *t) ++p; } - t->tv_sec = s * sign; + *ps = s * sign; /* Calculate nanoseconds. */ - t->tv_nsec = 0; + *pn = 0; if (*p != '.') return; @@ -1012,7 +1023,7 @@ pax_time(const wchar_t *p, struct timespec *t) do { ++p; if (*p >= '0' && *p <= '9') - t->tv_nsec += (*p - '0') * l; + *pn += (*p - '0') * l; else break; } while (l /= 10); diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index bd7d3b2c83fa..19317df0380d 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -515,19 +515,22 @@ archive_write_pax_header(struct archive *a, if (st_main->st_mtime < 0 || st_main->st_mtime >= 0x7fffffff || - st_main->st_mtimespec.tv_nsec != 0) + ARCHIVE_STAT_MTIME_NANOS(st_main) != 0) add_pax_attr_time(&(pax->pax_header), "mtime", - st_main->st_mtime, st_main->st_mtimespec.tv_nsec); + st_main->st_mtime, + ARCHIVE_STAT_MTIME_NANOS(st_main)); - if (st_main->st_ctimespec.tv_nsec != 0 || - st_main->st_ctime != 0) + if (st_main->st_ctime != 0 || + ARCHIVE_STAT_CTIME_NANOS(st_main) != 0) add_pax_attr_time(&(pax->pax_header), "ctime", - st_main->st_ctime, st_main->st_ctimespec.tv_nsec); + st_main->st_ctime, + ARCHIVE_STAT_CTIME_NANOS(st_main)); - if (st_main->st_atimespec.tv_nsec != 0 || - st_main->st_atime != 0) + if (st_main->st_atime != 0 || + ARCHIVE_STAT_ATIME_NANOS(st_main) != 0) add_pax_attr_time(&(pax->pax_header), "atime", - st_main->st_atime, st_main->st_atimespec.tv_nsec); + st_main->st_atime, + ARCHIVE_STAT_ATIME_NANOS(st_main)); /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags(entry_main);