+2005-06-21 Paul Eggert <eggert@cs.ucla.edu>
+
+ Further improvements inspired by Jim Meyering's fixes.
+
+ * NEWS: Better support for full-resolution time stamps.
+ The -v option now prints time stamps only to 1-minute resolution.
+ * gnulib.modules: Add utimens.
+ * lib/.cvsignore: Add imaxtostr.c, inttostr.c, inttostr.h,
+ offtostr.c, umaxtostr.c, utimens.c, utimens.h. Remove paxconvert.c.
+ * lib/Makefile.tmpl (libtar_a_SOURCES): Remove paxconvert.c.
+ * lib/paxconvert.c: Remove; superseded by umaxtostr.c.
+ * po/POTFILES.in: Remove lib/paxconvert.c. Add lib/xalloc-die.c,
+ lib/obstack.c.
+ * src/buffer.c (set_start_time, compute_duration, start_time):
+ Use gettime rather than rolling our own code.
+ * src/common.h (OLDGNU_NAME_FIELD_SIZE, MAXOCTAL11, MAXOCTAL7): Remove.
+ (newer_ctime_option): Remove.
+ (timespec_lt): New function.
+ (OLDER_STAT_TIME): Use it.
+ (string_to_chars): First arg is char const *, not char *.
+ (tartime): Time arg is now struct timespec. New bool arg.
+ All callers changed.
+ (code_ns_fraction): New decl.
+ (sys_stat_nanoseconds): Remove decl.
+ (get_stat_atime, get_stat_ctime, get_stat_mtime): New functions.
+ (set_stat_atime, set_stat_ctime, set_stat_mtime): New functions.
+ * src/compare.c: Include utimens.h rather than rolling our own.
+ (diff_dir, diff_file, diff_link, diff_symlink, diff_special):
+ Prototype.
+ (diff_dumpdir, diff_multivol): Prototype.
+ (diff_file): Support higher-resolution time stamps.
+ * src/create.c: Include utimens.h rather than rolling our own.
+ (MAX_OCTAL_VAL): New macro.
+ (tar_copy_str, string_to_chars): Don't bother to zero-fill;
+ the destination is already zeroed.
+ (string_to_chars): First arg is char const *.
+ (start_private_header): Use MINOR_TO_CHARS, not MAJOR_TO_CHARS,
+ for minor device number.
+ (write_header_name, dump_hard_link, dump_file0):
+ Simplify test for old GNU format.
+ (start_header): Put in placeholders for uid, etc., even when
+ using extended headers, for benefit of older "tar" implementations.
+ Don't assume uintmax_t is wider than 32 bits.
+ Output extended header for mtime if needed.
+ (dump_regular_finish, dump_file0):
+ Support extended time stamp resolution.
+ * src/extract.c: Include utimens.h rather than rolling our own.
+ (check_time): Support extended time stamp resolution.
+ * src/list.c: Include <inttostr.h>.
+ (tartime): Use umaxtostr rather than stringify_uintmax_t_backwards.
+ * src/xheader.c: Include <inttostr.h>.
+ Do not include <xstrtol.h>.
+ (strtoimax) [!HAVE_DECL_STRTOIMAX && !defined strtoimax]: New decl.
+ (strtoumax) [!HAVE_DECL_STRTOUMAX && !defined strtoumax]: New decl.
+ (BILLION, LOG10_BILLION): New constants.
+ (to_decimal): Remove; superseded by inttostr. All callers changed
+ to use umaxtostr.
+ (xheader_format_name): Don't assume pids and uintmax_t values
+ fit in 63 bytes (!) when printed.
+ (decode_record): Don't bother to check for ERANGE; an out of range
+ value must be treater than len_max anyway.
+ If the length is out of range, output it in the diagnostic.
+ (format_uintmax): Remove; all callers changed to use umaxtostr.
+ (xheader_print): Don't assume sizes can be printed in 99 bytes (!).
+ (out_of_range_header): New function.
+ (decode_time): Use it.
+ (code_time): Accept struct timespec, not time_t and unsigned long.
+ All callers changed. Size sbuf properly, and remove unnecessary check.
+ Don't assume time stamps can fit in 199 bytes.
+ Handle negative time stamps. Handle fractional time stamps
+ more consistently. Don't output unnecessary trailing zeros.
+ (decode_time): Yield struct timespec, not time_t and unsigned long.
+ All callers changed.
+ Handle negative time stamps. Truncate towards minus infinity
+ consistently. Improve overflow checks, and output a better
+ diagnostic on overflow.
+ (code_num): Don't assume uintmax_t can be printed in 99 bytes (!).
+ (decode_num): New function, for better diagnostics.
+ (atime_coder, atime_decoder, gid_decoder, ctime_coder):
+ (ctime_decoder, mtime_coder, mtime_decoder, size_decoder):
+ (uid_decoder, sparse_size_decoder, sparse_numblocks_decoder):
+ (sparse_offset_decoder, sparse_numbytes_decoder):
+ Use decode_num, etc., instead of xstrtoumax, etc.
+
+2005-06-21 Jim Meyering <jim@meyering.net>
+
+ Carefully crafted invalid headers can cause buffer overrun.
+ Invalid header fields go undiagnosed.
+ Some valid time strings are ignored.
+
+ * src/xheader.c (sparse_numblocks_decoder): Remove unchecked use
+ of `calloc'. Use xcalloc instead.
+ (decode_time, gid_decoder, size_decoder, uid_decoder):
+ (sparse_size_decoder, sparse_offset_decoder, sparse_numblocks_decoder):
+ Ensure that the result of calling xstrtoumax is no larger than
+ the maximum value for the target type. Upon any failure, exit with
+ a diagnostic.
+ (sparse_numblocks_decoder): Avoid buffer overrun/heap corruption:
+ use x2nrealloc, rather than `n *= 2' and xrealloc(p, n,....
+ (decode_time): Rewrite to accept time strings like
+ 1119018481.000000000. Before, such strings were always ignored.
+
2005-06-13 Sergey Poznyakoff <gray@Mirddin.farlep.net>
* src/create.c (dump_file0): Check for is_avoided_name()
* tests/update.at: New file
* tests/Makefile.am (TESTSUITE_AT): Add update.at
* tests/testsuite.at: Likewise
-
+
2005-06-13 Sergey Poznyakoff <gray@Mirddin.farlep.net>
* configure.ac (AC_STRUCT_ST_BLKSIZE)
is useful e.g. for processing output from `find dir -print0'.
An orthogonal option --unquote is provided as well.
+* Better support for full-resolution time stamps. Tar cannot restore
+time stamps to full nanosecond resolution, though, until the kernel
+guys get their act together and give us a system call to set file time
+stamps to nanosecond resolution.
+
+* The -v option now prints time stamps only to 1-minute resolution,
+not full resolution, to avoid using up too many output columns.
+Nanosecond resolution is now supported, but that would be too much.
+
* Bugfixes
** Allow non-option arguments to be interspersed with options.
unlinkdir
unlocked-io
utime
+utimens
xalloc
xalloc-die
xgetcwd
hash.h
human.c
human.h
+imaxtostr.c
intprops.h
+inttostr.c
+inttostr.h
lchown.c
lchown.h
localcharset.c
modechange.h
obstack.c
obstack.h
+offtostr.c
openat.c
openat.h
pathmax.h
-paxconvert.c
paxerror.c
paxexit.c
paxlib.h
time_r.c
time_r.h
timespec.h
+umaxtostr.c
unistd-safer.h
unlinkdir.c
unlinkdir.h
unlocked-io.h
unsetenv.c
utime.c
+utimens.c
+utimens.h
vasnprintf.c
vasnprintf.h
vsnprintf.c
noinst_LIBRARIES = libtar.a
noinst_HEADERS = system.h localedir.h rmt.h paxlib.h
-libtar_a_SOURCES = prepargs.c prepargs.h rtapelib.c paxerror.c paxexit.c paxconvert.c paxnames.c
+libtar_a_SOURCES = prepargs.c prepargs.h rtapelib.c paxerror.c paxexit.c paxnames.c
localedir = $(datadir)/locale
# List of files which contain translatable strings.
-# Copyright (C) 1996, 1999, 2000, 2003 Free Software Foundation, Inc.
+# Copyright (C) 1996, 1999, 2000, 2003, 2004, 2005 Free Software
+# Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# Library files
lib/argmatch.c
+lib/argp-help.c
+lib/argp-parse.c
lib/error.c
lib/getopt.c
lib/human.c
-lib/quotearg.c
-lib/xmalloc.c
-lib/rtapelib.c
-lib/argp-help.c
-lib/paxconvert.c
+lib/obstack.c
lib/paxerror.c
lib/paxexit.c
lib/paxnames.c
+lib/quotearg.c
+lib/rtapelib.c
+lib/xalloc-die.c
+lib/xmalloc.c
rmt/rmt.c
# Testsuite
tests/genfile.c
-
void
set_start_time ()
{
-#if HAVE_CLOCK_GETTIME
- if (clock_gettime (CLOCK_REALTIME, &start_timespec) != 0)
-#endif
- start_time = time (0);
+ gettime (&start_time);
}
void
compute_duration ()
{
-#if HAVE_CLOCK_GETTIME
struct timespec now;
- if (clock_gettime (CLOCK_REALTIME, &now) == 0)
- duration += ((now.tv_sec - start_timespec.tv_sec)
- + (now.tv_nsec - start_timespec.tv_nsec) / 1e9);
- else
-#endif
- duration += time (NULL) - start_time;
+ gettime (&now);
+ duration += ((now.tv_sec - start_time.tv_sec)
+ + (now.tv_nsec - start_time.tv_nsec) / 1e9);
set_start_time ();
}
strip_trailing_slashes (current_stat_info.file_name);
record_start->header.typeflag = GNUTYPE_VOLHDR;
- TIME_TO_CHARS (start_time, record_start->header.mtime);
+ TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime);
finish_header (¤t_stat_info, record_start, -1);
}
break;
{
if (save_name)
{
- assign_string (&real_s_name,
- safer_name_suffix (save_name, false,
+ assign_string (&real_s_name,
+ safer_name_suffix (save_name, false,
absolute_names_option));
real_s_totsize = save_totsize;
real_s_sizeleft = save_sizeleft;
memset (record_start, 0, BLOCKSIZE);
sprintf (record_start->header.name, "%s Volume %d",
volume_label_option, volno);
- TIME_TO_CHARS (start_time, record_start->header.mtime);
+ TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime);
record_start->header.typeflag = GNUTYPE_VOLHDR;
finish_header (¤t_stat_info, record_start, -1);
}
assign_string (&real_s_name, 0);
else
{
- assign_string (&real_s_name,
+ assign_string (&real_s_name,
safer_name_suffix (save_name, false,
absolute_names_option));
real_s_sizeleft = save_sizeleft;
{
if (save_name)
{
- assign_string (&real_s_name,
+ assign_string (&real_s_name,
safer_name_suffix (save_name, false,
absolute_names_option));
real_s_sizeleft = save_sizeleft;
/* The checksum field is filled with this while the checksum is computed. */
#define CHKBLANKS " " /* 8 blanks, no null */
-/* Old GNU stores zero-terminated file name */
-#define OLDGNU_NAME_FIELD_SIZE 99
-
/* Some constants from POSIX are given names. */
#define NAME_FIELD_SIZE 100
#define PREFIX_FIELD_SIZE 155
#define UNAME_FIELD_SIZE 32
#define GNAME_FIELD_SIZE 32
-/* FIXME */
-#define MAXOCTAL11 017777777777L
-#define MAXOCTAL7 07777777
-
\f
/* Some various global definitions. */
GLOBAL bool multi_volume_option;
-/* The same variable holds the time, whether mtime or ctime. Just fake a
- non-existing option, for making the code clearer, elsewhere. */
-#define newer_ctime_option newer_mtime_option
-
/* Specified threshold date and time. Files having an older time stamp
do not get archived (also see after_date_option above). */
GLOBAL struct timespec newer_mtime_option;
/* Return true if the struct stat ST's M time is less than
newer_mtime_option. */
#define OLDER_STAT_TIME(st, m) \
- ((st).st_##m##time < newer_mtime_option.tv_sec \
- || ((st).st_##m##time == newer_mtime_option.tv_sec \
- && TIMESPEC_NS ((st).st_##m##tim) < newer_mtime_option.tv_nsec))
+ timespec_lt (get_stat_##m##time (&st), newer_mtime_option)
+
+/* Return true if A < B. */
+static inline
+timespec_lt (struct timespec a, struct timespec b)
+{
+ return a.tv_sec < b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec);
+}
/* Zero if there is no recursion, otherwise FNM_LEADING_DIR. */
GLOBAL int recursion_option;
GLOBAL bool dev_null_output;
/* Timestamp for when we started execution. */
-#if HAVE_CLOCK_GETTIME
- GLOBAL struct timespec start_timespec;
-# define start_time (start_timespec.tv_sec)
-#else
- GLOBAL time_t start_time;
-#endif
+GLOBAL struct timespec start_time;
GLOBAL struct tar_stat_info current_stat_info;
void time_to_chars (time_t, char *, size_t);
void uid_to_chars (uid_t, char *, size_t);
void uintmax_to_chars (uintmax_t, char *, size_t);
-void string_to_chars (char *, char *, size_t);
+void string_to_chars (char const *, char *, size_t);
/* Module diffarch.c. */
void decode_header (union block *, struct tar_stat_info *,
enum archive_format *, int);
-char const *tartime (time_t);
+char const *tartime (struct timespec, bool);
#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
char *quote_copy_string (const char *);
int unquote_string (char *);
+void code_ns_fraction (int, char *);
+
size_t dot_dot_prefix_len (char const *);
enum remove_option
/* Module system.c */
-void sys_stat_nanoseconds (struct tar_stat_info *);
void sys_detect_dev_null_output (void);
void sys_save_archive_dev_ino (void);
void sys_drain_input_pipe (void);
/* Module utf8.c */
bool string_ascii_p (const char *str);
bool utf8_convert (bool to_utf, char const *input, char **output);
+
+
+/* FIXME: The following should get moved into gnulib. */
+
+static inline struct timespec
+get_stat_atime (struct stat const *st)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ return st->st_atim;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ return st->st_atimespec;
+#else
+ struct timespec t;
+ t.tv_sec = st->st_atime;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ t.tv_nsec = st->stat.st_atimensec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ t.tv_nsec = st->stat.st_spare1 * 1000;
+# else
+ t.tv_nsec = 0;
+# endif
+ return t;
+#endif
+}
+
+static inline struct timespec
+get_stat_ctime (struct stat const *st)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ return st->st_ctim;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ return st->st_ctimespec;
+#else
+ struct timespec t;
+ t.tv_sec = st->st_ctime;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ t.tv_nsec = st->stat.st_ctimensec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ t.tv_nsec = st->stat.st_spare1 * 1000;
+# else
+ t.tv_nsec = 0;
+# endif
+ return t;
+#endif
+}
+
+static inline struct timespec
+get_stat_mtime (struct stat const *st)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ return st->st_mtim;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ return st->st_mtimespec;
+#else
+ struct timespec t;
+ t.tv_sec = st->st_mtime;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ t.tv_nsec = st->stat.st_mtimensec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ t.tv_nsec = st->stat.st_spare1 * 1000;
+# else
+ t.tv_nsec = 0;
+# endif
+ return t;
+#endif
+}
+
+static inline void
+set_stat_atime (struct stat *st, struct timespec t)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ st->st_atim = t;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ st->st_atimespec = t;
+#else
+ st->st_atime = t.tv_sec;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ st->stat.st_atimensec = t.tv_nsec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ st->stat.st_spare1 = t.tv_nsec / 1000;
+# endif
+#endif
+}
+
+static inline void
+set_stat_ctime (struct stat *st, struct timespec t)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ st->st_ctim = t;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ st->st_ctimespec = t;
+#else
+ st->st_ctime = t.tv_sec;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ st->stat.st_ctimensec = t.tv_nsec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ st->stat.st_spare1 = t.tv_nsec / 1000;
+# endif
+#endif
+}
+
+static inline void
+set_stat_mtime (struct stat *st, struct timespec t)
+{
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ st->st_mtim = t;
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+ st->st_mtimespec = t;
+#else
+ st->st_mtime = t.tv_sec;
+# if defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+ st->stat.st_mtimensec = t.tv_nsec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ st->stat.st_spare1 = t.tv_nsec / 1000;
+# endif
+#endif
+}
/* Diff files from a tar archive.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004 Free Software Foundation, Inc.
+ 2003, 2004, 2005 Free Software Foundation, Inc.
Written by John Gilmore, on 1987-04-30.
#include <system.h>
-#if HAVE_UTIME_H
-# include <utime.h>
-#else
-struct utimbuf
- {
- long actime;
- long modtime;
- };
-#endif
-
#if HAVE_LINUX_FD_H
# include <linux/fd.h>
#endif
#include <quotearg.h>
+#include <utimens.h>
#include "common.h"
#include <rmt.h>
\f
static void
-diff_dir ()
+diff_dir (void)
{
struct stat stat_data;
}
static void
-diff_file ()
+diff_file (void)
{
struct stat stat_data;
else
{
int status;
- struct utimbuf restore_times;
-
- restore_times.actime = stat_data.st_atime;
- restore_times.modtime = stat_data.st_mtime;
if (current_stat_info.is_sparse)
sparse_diff_file (diff_handle, ¤t_stat_info);
{
if (multi_volume_option)
{
- assign_string (&save_name,
+ assign_string (&save_name,
current_stat_info.orig_file_name);
save_totsize = current_stat_info.stat.st_size;
/* save_sizeleft is set in read_and_process. */
close_error (current_stat_info.file_name);
if (atime_preserve_option)
- utime (current_stat_info.file_name, &restore_times);
+ {
+ struct timespec ts[2];
+ ts[0] = get_stat_atime (&stat_data);
+ ts[1] = get_stat_mtime (&stat_data);
+ if (utimens (current_stat_info.file_name, ts) != 0)
+ utime_error (current_stat_info.file_name);
+ }
}
}
}
}
static void
-diff_link ()
+diff_link (void)
{
struct stat file_data;
struct stat link_data;
#ifdef HAVE_READLINK
static void
-diff_symlink ()
+diff_symlink (void)
{
size_t len = strlen (current_stat_info.link_name);
char *linkbuf = alloca (len + 1);
#endif
static void
-diff_special ()
+diff_special (void)
{
struct stat stat_data;
}
static void
-diff_dumpdir ()
+diff_dumpdir (void)
{
char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name,
0);
}
static void
-diff_multivol ()
+diff_multivol (void)
{
struct stat stat_data;
int fd, status;
diff_dir ();
return;
}
-
+
if (!get_stat_data (current_stat_info.file_name, &stat_data))
return;
}
fd = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
-
+
if (fd < 0)
{
open_error (current_stat_info.file_name);
if (multi_volume_option)
assign_string (&save_name, 0);
-
+
status = close (fd);
if (status != 0)
close_error (current_stat_info.file_name);
diff_symlink ();
break;
#endif
-
+
case CHRTYPE:
case BLKTYPE:
case FIFOTYPE:
case GNUTYPE_DUMPDIR:
diff_dumpdir ();
/* Fall through. */
-
+
case DIRTYPE:
diff_dir ();
break;
#include <system.h>
-#if HAVE_UTIME_H
-# include <utime.h>
-#else
-struct utimbuf
- {
- long actime;
- long modtime;
- };
-#endif
-
#include <quotearg.h>
+#include <utimens.h>
#include "common.h"
#include <hash.h>
? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \
: (uintmax_t) -1)
+/* The maximum uintmax_t value that can be represented with octal
+ digits and a trailing NUL in BUFFER. */
+#define MAX_OCTAL_VAL(buffer) MAX_VAL_WITH_DIGITS (sizeof (buffer) - 1, LG_8)
+
/* Convert VALUE to an octal representation suitable for tar headers.
Output to buffer WHERE with size SIZE.
The result is undefined if SIZE is 0 or if VALUE is too large to fit. */
while (i);
}
+/* Copy at most LEN bytes from the string SRC to DST. Terminate with
+ NUL unless SRC is LEN or more bytes long. */
+
+static void
+tar_copy_str (char *dst, const char *src, size_t len)
+{
+ size_t i;
+ for (i = 0; i < len; i++)
+ if (! (dst[i] = src[i]))
+ break;
+}
+
+/* Same as tar_copy_str, but always terminate with NUL if using
+ is OLDGNU format */
+
+static void
+tar_name_copy_str (char *dst, const char *src, size_t len)
+{
+ tar_copy_str (dst, src, len);
+ if (archive_format == OLDGNU_FORMAT)
+ dst[len-1] = 0;
+}
+
/* Convert NEGATIVE VALUE to a base-256 representation suitable for
tar headers. NEGATIVE is 1 if VALUE was negative before being cast
to uintmax_t, 0 otherwise. Output to buffer WHERE with size SIZE.
}
void
-string_to_chars (char *str, char *p, size_t s)
+string_to_chars (char const *str, char *p, size_t s)
{
- strncpy (p, str, s);
- p[s-1] = 0;
+ tar_copy_str (p, str, s);
+ p[s - 1] = '\0';
}
\f
set_next_block_after (pointer);
}
-/* Copy at most LEN bytes from SRC to DST. Terminate with NUL unless
- SRC is LEN characters long */
-static void
-tar_copy_str (char *dst, const char *src, size_t len)
-{
- dst[len-1] = 0;
- strncpy (dst, src, len);
-}
-
-/* Same as tar_copy_str, but always terminate with NUL if using
- is OLDGNU format */
-static void
-tar_name_copy_str (char *dst, const char *src, size_t len)
-{
- tar_copy_str (dst, src, len);
- if (archive_format == OLDGNU_FORMAT)
- dst[len-1] = 0;
-}
-
/* Write a "private" header */
union block *
start_private_header (const char *name, size_t size)
UID_TO_CHARS (getuid (), header->header.uid);
GID_TO_CHARS (getgid (), header->header.gid);
MAJOR_TO_CHARS (0, header->header.devmajor);
- MAJOR_TO_CHARS (0, header->header.devminor);
+ MINOR_TO_CHARS (0, header->header.devminor);
strncpy (header->header.magic, TMAGIC, TMAGLEN);
strncpy (header->header.version, TVERSION, TVERSLEN);
return header;
xheader_store ("path", st, NULL);
return write_short_name (st);
}
- else if ((archive_format == OLDGNU_FORMAT
- && OLDGNU_NAME_FIELD_SIZE < strlen (st->file_name))
- || NAME_FIELD_SIZE < strlen (st->file_name))
+ else if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT)
+ < strlen (st->file_name))
return write_long_name (st);
else
return write_short_name (st);
else
MODE_TO_CHARS (st->stat.st_mode, header->header.mode);
- if (st->stat.st_uid > MAXOCTAL7 && archive_format == POSIX_FORMAT)
- xheader_store ("uid", st, NULL);
- else
- UID_TO_CHARS (st->stat.st_uid, header->header.uid);
+ {
+ uid_t uid = st->stat.st_uid;
+ if (archive_format == POSIX_FORMAT
+ && MAX_OCTAL_VAL (header->header.uid) < uid)
+ {
+ xheader_store ("uid", st, NULL);
+ uid = 0;
+ }
+ UID_TO_CHARS (uid, header->header.uid);
+ }
- if (st->stat.st_gid > MAXOCTAL7 && archive_format == POSIX_FORMAT)
- xheader_store ("gid", st, NULL);
- else
- GID_TO_CHARS (st->stat.st_gid, header->header.gid);
+ {
+ gid_t gid = st->stat.st_gid;
+ if (archive_format == POSIX_FORMAT
+ && MAX_OCTAL_VAL (header->header.gid) < gid)
+ {
+ xheader_store ("gid", st, NULL);
+ gid = 0;
+ }
+ GID_TO_CHARS (gid, header->header.gid);
+ }
- if (st->stat.st_size > MAXOCTAL11 && archive_format == POSIX_FORMAT)
- xheader_store ("size", st, NULL);
- else
- OFF_TO_CHARS (st->stat.st_size, header->header.size);
+ {
+ off_t size = st->stat.st_size;
+ if (archive_format == POSIX_FORMAT
+ && MAX_OCTAL_VAL (header->header.size) < size)
+ {
+ xheader_store ("size", st, NULL);
+ size = 0;
+ }
+ OFF_TO_CHARS (size, header->header.size);
+ }
- TIME_TO_CHARS (st->stat.st_mtime, header->header.mtime);
+ {
+ struct timespec mtime = get_stat_mtime (&st->stat);
+ if (archive_format == POSIX_FORMAT)
+ {
+ if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec
+ || mtime.tv_nsec != 0)
+ xheader_store ("mtime", st, NULL);
+ if (MAX_OCTAL_VAL (header->header.mtime) < mtime.tv_sec)
+ mtime.tv_sec = 0;
+ }
+ TIME_TO_CHARS (mtime.tv_sec, header->header.mtime);
+ }
/* FIXME */
if (S_ISCHR (st->stat.st_mode)
|| S_ISBLK (st->stat.st_mode))
{
- st->devmajor = major (st->stat.st_rdev);
- st->devminor = minor (st->stat.st_rdev);
+ major_t devmajor = major (st->stat.st_rdev);
+ minor_t devminor = minor (st->stat.st_rdev);
- if (st->devmajor > MAXOCTAL7 && archive_format == POSIX_FORMAT)
- xheader_store ("devmajor", st, NULL);
- else
- MAJOR_TO_CHARS (st->devmajor, header->header.devmajor);
+ if (archive_format == POSIX_FORMAT
+ && MAX_OCTAL_VAL (header->header.devmajor) < devmajor)
+ {
+ xheader_store ("devmajor", st, NULL);
+ devmajor = 0;
+ }
+ MAJOR_TO_CHARS (devmajor, header->header.devmajor);
- if (st->devminor > MAXOCTAL7 && archive_format == POSIX_FORMAT)
- xheader_store ("devminor", st, NULL);
- else
- MAJOR_TO_CHARS (st->devminor, header->header.devminor);
+ if (archive_format == POSIX_FORMAT
+ && MAX_OCTAL_VAL (header->header.devminor) < devminor)
+ {
+ xheader_store ("devminor", st, NULL);
+ devminor = 0;
+ }
+ MINOR_TO_CHARS (devminor, header->header.devminor);
}
else if (archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT)
{
&& (strlen (st->uname) > UNAME_FIELD_SIZE
|| !string_ascii_p (st->uname)))
xheader_store ("uname", st, NULL);
- else
- UNAME_TO_CHARS (st->uname, header->header.uname);
+ UNAME_TO_CHARS (st->uname, header->header.uname);
if (archive_format == POSIX_FORMAT
&& (strlen (st->gname) > GNAME_FIELD_SIZE
|| !string_ascii_p (st->gname)))
xheader_store ("gname", st, NULL);
- else
- GNAME_TO_CHARS (st->gname, header->header.gname);
+ GNAME_TO_CHARS (st->gname, header->header.gname);
}
return header;
size_left -= count;
if (count)
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
-
+
if (count != bufsize)
{
char buf[UINTMAX_STRSIZE_BOUND];
}
static void
-dump_regular_finish (int fd, struct tar_stat_info *st, time_t original_ctime)
+dump_regular_finish (int fd, struct tar_stat_info *st,
+ struct timespec original_ctime)
{
if (fd >= 0)
{
{
stat_diag (st->orig_file_name);
}
- else if (final_stat.st_ctime != original_ctime)
+ else if (final_stat.st_ctime != original_ctime.tv_sec
+ || (get_stat_ctime (&final_stat).tv_nsec
+ != original_ctime.tv_nsec))
{
WARN ((0, 0, _("%s: file changed as we read it"),
quotearg_colon (st->orig_file_name)));
block_ordinal = current_block_ordinal ();
assign_string (&st->link_name, link_name);
- if ((archive_format == OLDGNU_FORMAT
- && OLDGNU_NAME_FIELD_SIZE < strlen (link_name))
- || NAME_FIELD_SIZE < strlen (link_name))
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT)
+ < strlen (link_name))
write_long_link (st);
st->stat.st_size = 0;
{
union block *header;
char type;
- time_t original_ctime;
- struct utimbuf restore_times;
+ struct timespec original_ctime;
+ struct timespec restore_times[2];
off_t block_ordinal = -1;
if (interactive_option && !confirm ("add", p))
return;
assign_string (&st->orig_file_name, p);
- assign_string (&st->file_name,
+ assign_string (&st->file_name,
safer_name_suffix (p, false, absolute_names_option));
if (deref_stat (dereference_option, p, &st->stat) != 0)
return;
}
st->archive_file_size = st->stat.st_size;
- sys_stat_nanoseconds (st);
- original_ctime = st->stat.st_ctime;
- restore_times.actime = st->stat.st_atime;
- restore_times.modtime = st->stat.st_mtime;
+ original_ctime = get_stat_ctime (&st->stat);
+ restore_times[0] = get_stat_atime (&st->stat);
+ restore_times[1] = get_stat_mtime (&st->stat);
#ifdef S_ISHIDDEN
if (S_ISHIDDEN (st->stat.st_mode))
{
dump_dir (st, top_level, parent_device);
if (atime_preserve_option)
- utime (p, &restore_times);
+ utimens (p, restore_times);
return;
}
else
}
if (atime_preserve_option)
- utime (st->orig_file_name, &restore_times);
+ utimens (st->orig_file_name, restore_times);
file_count_links (st);
return;
}
}
buffer[size] = '\0';
assign_string (&st->link_name, buffer);
- if ((archive_format == OLDGNU_FORMAT && size > OLDGNU_NAME_FIELD_SIZE)
- || size > NAME_FIELD_SIZE)
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
write_long_link (st);
block_ordinal = current_block_ordinal ();
/* Extract files from a tar archive.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
- 2001, 2003, 2004 Free Software Foundation, Inc.
+ 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-11-19.
#include <system.h>
#include <quotearg.h>
+#include <utimens.h>
#include <errno.h>
#include <xgetcwd.h>
-#if HAVE_UTIME_H
-# include <utime.h>
-#else
-struct utimbuf
- {
- long actime;
- long modtime;
- };
-#endif
-
#include "common.h"
static bool we_are_root; /* true if our effective uid == 0 */
/* Check time after successfully setting FILE_NAME's time stamp to T. */
static void
-check_time (char const *file_name, time_t t)
+check_time (char const *file_name, struct timespec t)
{
- time_t now;
- if (t <= 0)
+ if (t.tv_sec <= 0)
WARN ((0, 0, _("%s: implausibly old time stamp %s"),
- file_name, tartime (t)));
- else if (start_time < t && (now = time (0)) < t)
- WARN ((0, 0, _("%s: time stamp %s is %lu s in the future"),
- file_name, tartime (t), (unsigned long) (t - now)));
+ file_name, tartime (t, true)));
+ else if (timespec_lt (start_time, t))
+ {
+ struct timespec now;
+ gettime (&now);
+ if (timespec_lt (now, t))
+ {
+ unsigned long int ds = t.tv_sec - now.tv_sec;
+ int dns = t.tv_nsec - now.tv_nsec;
+ char dnsbuf[sizeof ".FFFFFFFFF"];
+ if (dns < 0)
+ {
+ dns += 1000000000;
+ ds--;
+ }
+ code_ns_fraction (dns, dnsbuf);
+ WARN ((0, 0, _("%s: time stamp %s is %lu%s s in the future"),
+ file_name, tartime (t, true), ds, dnsbuf));
+ }
+ }
}
/* Restore stat attributes (owner, group, mode and times) for
mode_t invert_permissions, enum permstatus permstatus,
char typeflag)
{
- struct utimbuf utimbuf;
-
if (typeflag != SYMTYPE)
{
/* We do the utime before the chmod because some versions of utime are
/* FIXME: incremental_option should set ctime too, but how? */
- if (incremental_option)
- utimbuf.actime = stat_info->st_atime;
- else
- utimbuf.actime = start_time;
-
- utimbuf.modtime = stat_info->st_mtime;
+ struct timespec ts[2];
+ ts[0] = incremental_option ? get_stat_atime (stat_info) : start_time;
+ ts[1] = get_stat_mtime (stat_info);
- if (utime (file_name, &utimbuf) < 0)
+ if (utimens (file_name, ts) != 0)
utime_error (file_name);
else
{
- check_time (file_name, utimbuf.actime);
- check_time (file_name, utimbuf.modtime);
+ check_time (file_name, ts[0]);
+ check_time (file_name, ts[1]);
}
}
static int
extract_link (char *file_name, int typeflag)
{
- char const *link_name = safer_name_suffix (current_stat_info.link_name,
+ char const *link_name = safer_name_suffix (current_stat_info.link_name,
true, absolute_names_option);
int interdir_made = 0;
if (verbose_option)
print_header (¤t_stat_info, -1);
- file_name = safer_name_suffix (current_stat_info.file_name,
+ file_name = safer_name_suffix (current_stat_info.file_name,
false, absolute_names_option);
if (strip_name_components)
{
_("Time stamp out of range")));
else
{
+ /* FIXME: This should also input nanoseconds, but that will be a
+ change in file format. */
newer_mtime_option.tv_sec = t;
newer_mtime_option.tv_nsec = 0;
}
if (sys_truncate (fileno (fp)) != 0)
truncate_error (listed_incremental_option);
- fprintf (fp, "%lu\n", (unsigned long) start_time);
+ /* FIXME: This should also output nanoseconds, but that will be a
+ change in file format. */
+ fprintf (fp, "%lu\n", (unsigned long int) start_time.tv_sec);
if (! ferror (fp) && directory_table)
hash_do_for_each (directory_table, write_directory_file_entry, fp);
if (ferror (fp))
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-/* Define to non-zero for forcing old ctime format instead of ISO format. */
-#undef USE_OLD_CTIME
-
#include <system.h>
+#include <inttostr.h>
#include <quotearg.h>
#include "common.h"
{
enum read_header status = HEADER_STILL_UNREAD;
enum read_header prev_status;
+ struct timespec mtime;
base64_init ();
name_gather ();
|| (NEWER_OPTION_INITIALIZED (newer_mtime_option)
/* FIXME: We get mtime now, and again later; this causes
duplicate diagnostics if header.mtime is bogus. */
- && ((current_stat_info.stat.st_mtime
+ && ((mtime.tv_sec
= TIME_FROM_HEADER (current_header->header.mtime)),
-#ifdef ST_MTIM_NSEC
/* FIXME: Grab fractional time stamps from
extended header. */
- current_stat_info.stat.st_mtim.ST_MTIM_NSEC = 0,
-#endif
+ mtime.tv_nsec = 0,
+ set_stat_mtime (¤t_stat_info.stat, mtime),
OLDER_STAT_TIME (current_stat_info.stat, m)))
|| excluded_name (current_stat_info.file_name))
{
enum archive_format *format_pointer, int do_user_group)
{
enum archive_format format;
+ struct timespec atime;
+ struct timespec ctime;
+ struct timespec mtime;
if (strcmp (header->header.magic, TMAGIC) == 0)
{
*format_pointer = format;
stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
- stat_info->stat.st_mtime = TIME_FROM_HEADER (header->header.mtime);
+ mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
+ mtime.tv_nsec = 0;
+ set_stat_mtime (&stat_info->stat, mtime);
assign_string (&stat_info->uname,
header->header.uname[0] ? header->header.uname : NULL);
assign_string (&stat_info->gname,
header->header.gname[0] ? header->header.gname : NULL);
- stat_info->devmajor = MAJOR_FROM_HEADER (header->header.devmajor);
- stat_info->devminor = MINOR_FROM_HEADER (header->header.devminor);
-
- stat_info->stat.st_atime = start_time;
- stat_info->stat.st_ctime = start_time;
if (format == OLDGNU_FORMAT && incremental_option)
{
- stat_info->stat.st_atime = TIME_FROM_HEADER (header->oldgnu_header.atime);
- stat_info->stat.st_ctime = TIME_FROM_HEADER (header->oldgnu_header.ctime);
+ atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
+ ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime);
+ atime.tv_nsec = ctime.tv_nsec = 0;
+ }
+ else if (format == STAR_FORMAT)
+ {
+ atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime);
+ ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime);
+ atime.tv_nsec = ctime.tv_nsec = 0;
}
+ else
+ atime = ctime = start_time;
+
+ set_stat_atime (&stat_info->stat, atime);
+ set_stat_ctime (&stat_info->stat, ctime);
if (format == V7_FORMAT)
{
}
else
{
-
- if (format == STAR_FORMAT)
- {
- stat_info->stat.st_atime = TIME_FROM_HEADER (header->star_header.atime);
- stat_info->stat.st_ctime = TIME_FROM_HEADER (header->star_header.ctime);
- }
-
if (do_user_group)
{
/* FIXME: Decide if this should somewhat depend on -p. */
{
case BLKTYPE:
case CHRTYPE:
- stat_info->stat.st_rdev = makedev (stat_info->devmajor,
- stat_info->devminor);
+ stat_info->stat.st_rdev =
+ makedev (MAJOR_FROM_HEADER (header->header.devmajor),
+ MINOR_FROM_HEADER (header->header.devminor));
break;
default:
/* Return a printable representation of T. The result points to
static storage that can be reused in the next call to this
- function, to ctime, or to asctime. */
+ function, to ctime, or to asctime. If FULL_TIME, then output the
+ time stamp to its full resolution; otherwise, just output it to
+ 1-minute resolution. */
char const *
-tartime (time_t t)
+tartime (struct timespec t, bool full_time)
{
+ enum { fraclen = sizeof ".FFFFFFFFF" - 1 };
static char buffer[max (UINTMAX_STRSIZE_BOUND + 1,
- INT_STRLEN_BOUND (int) + 16)];
+ INT_STRLEN_BOUND (int) + 16)
+ + fraclen];
+ struct tm *tm;
+ time_t s = t.tv_sec;
+ int ns = t.tv_nsec;
+ bool negative = s < 0;
char *p;
-#if USE_OLD_CTIME
- p = ctime (&t);
- if (p)
+ if (negative && ns != 0)
{
- char const *time_stamp = p + 4;
- for (p += 16; p[3] != '\n'; p++)
- p[0] = p[3];
- p[0] = '\0';
- return time_stamp;
+ s++;
+ ns = 1000000000 - ns;
}
-#else
- /* Use ISO 8610 format. See:
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html */
- struct tm *tm = utc_option ? gmtime (&t) : localtime (&t);
+
+ tm = utc_option ? gmtime (&s) : localtime (&s);
if (tm)
{
- sprintf (buffer, "%04ld-%02d-%02d %02d:%02d:%02d",
- tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ if (full_time)
+ {
+ sprintf (buffer, "%04ld-%02d-%02d %02d:%02d:%02d",
+ tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ code_ns_fraction (ns, buffer + strlen (buffer));
+ }
+ else
+ sprintf (buffer, "%04ld-%02d-%02d %02d:%02d",
+ tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min);
return buffer;
}
-#endif
/* The time stamp cannot be broken down, most likely because it
is out of range. Convert it as an integer,
right-adjusted in a field with the same width as the usual
- 19-byte 4-year ISO time format. */
- p = stringify_uintmax_t_backwards (t < 0 ? - (uintmax_t) t : (uintmax_t) t,
- buffer + sizeof buffer);
- if (t < 0)
+ 4-year ISO time format. */
+ p = umaxtostr (negative ? - (uintmax_t) s : s,
+ buffer + sizeof buffer - UINTMAX_STRSIZE_BOUND - fraclen);
+ if (negative)
*--p = '-';
- while (buffer + sizeof buffer - 19 - 1 < p)
+ while ((buffer + sizeof buffer - sizeof "YYYY-MM-DD HH:MM"
+ + (full_time ? sizeof ":SS.FFFFFFFFF" - 1 : 0))
+ < p)
*--p = ' ';
+ if (full_time)
+ code_ns_fraction (ns, buffer + sizeof buffer - 1 - fraclen);
return p;
}
/* FIXME: Note that print_header uses the globals HEAD, HSTAT, and
HEAD_STANDARD, which must be set up in advance. Not very clean.. */
-/* UGSWIDTH starts with 18, so with user and group names <= 8 chars, the
- columns never shift during the listing. */
-#define UGSWIDTH 18
-static int ugswidth = UGSWIDTH; /* maximum width encountered so far */
+/* Width of "user/group size", with initial value chosen
+ heuristically. This grows as needed, though this may cause some
+ stairstepping in the output. Make it too small and the output will
+ almost always look ragged. Make it too large and the output will
+ be spaced out too far. */
+static int ugswidth = 19;
-/* DATEWIDTH is the number of columns taken by the date and time fields. */
-#if USE_OLD_CDATE
-# define DATEWIDTH 19
-#else
-# define DATEWIDTH 18
-#endif
+/* Width of printed time stamps. It grows if longer time stamps are
+ found (typically, those with nanosecond resolution). Like
+ USGWIDTH, some stairstepping may occur. */
+static int datewidth = sizeof "YYYY-MM-DD HH:MM" - 1;
void
print_header (struct tar_stat_info *st, off_t block_ordinal)
{
char modes[11];
char const *time_stamp;
+ int time_stamp_len;
char *temp_name = st->orig_file_name ? st->orig_file_name : st->file_name;
/* These hold formatted ints. */
/* holds formatted size or major,minor */
char uintbuf[UINTMAX_STRSIZE_BOUND];
int pad;
+ int sizelen;
if (block_number_option)
{
/* Time stamp. */
- time_stamp = tartime (st->stat.st_mtime);
+ time_stamp = tartime (get_stat_mtime (&st->stat), false);
+ time_stamp_len = strlen (time_stamp);
+ if (datewidth < time_stamp_len)
+ datewidth = time_stamp_len;
/* User and group names. */
/* Figure out padding and print the whole line. */
- pad = strlen (user) + strlen (group) + strlen (size) + 1;
+ sizelen = strlen (size);
+ pad = strlen (user) + 1 + strlen (group) + 1 + sizelen;
if (pad > ugswidth)
ugswidth = pad;
- fprintf (stdlis, "%s %s/%s %*s%s %s",
- modes, user, group, ugswidth - pad, "", size, time_stamp);
+ fprintf (stdlis, "%s %s/%s %*s %-*s",
+ modes, user, group, ugswidth - pad + sizelen, size,
+ datewidth, time_stamp);
fprintf (stdlis, " %s", quotearg (temp_name));
STRINGIFY_BIGINT (current_block_ordinal (), buf));
}
- fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH,
+ fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + 1 + datewidth,
_("Creating directory:"), length, quotearg (dirname));
}
}
return result;
}
\f
+/* Handling numbers. */
+
+/* Output fraction and trailing digits appropriate for a nanoseconds
+ count equal to NS, but don't output unnecessary '.' or trailing
+ zeros. */
+
+void
+code_ns_fraction (int ns, char *p)
+{
+ if (ns == 0)
+ *p = '\0';
+ else
+ {
+ int i = 9;
+ *p++ = '.';
+
+ while (ns % 10 == 0)
+ {
+ ns /= 10;
+ i--;
+ }
+
+ p[i] = '\0';
+
+ for (;;)
+ {
+ p[--i] = '0' + ns % 10;
+ if (i == 0)
+ break;
+ ns /= 10;
+ }
+ }
+}
+\f
/* File handling. */
/* Saved names in case backup needs to be undone. */
#include <rmt.h>
#include <signal.h>
-void
-sys_stat_nanoseconds (struct tar_stat_info *st)
-{
-#if defined(HAVE_STRUCT_STAT_ST_SPARE1)
- st->atime_nsec = st->stat.st_spare1 * 1000;
- st->mtime_nsec = st->stat.st_spare2 * 1000;
- st->ctime_nsec = st->stat.st_spare3 * 1000;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
- st->atime_nsec = st->stat.st_atim.tv_nsec;
- st->mtime_nsec = st->stat.st_mtim.tv_nsec;
- st->ctime_nsec = st->stat.st_ctim.tv_nsec;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
- st->atime_nsec = st->stat.st_atimespec.tv_nsec;
- st->mtime_nsec = st->stat.st_mtimespec.tv_nsec;
- st->ctime_nsec = st->stat.st_ctimespec.tv_nsec;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
- st->atime_nsec = st->stat.st_atimensec;
- st->mtime_nsec = st->stat.st_mtimensec;
- st->ctime_nsec = st->stat.st_ctimensec;
-#else
- st->atime_nsec = st->mtime_nsec = st->ctime_nsec = 0;
-#endif
-}
-
#if MSDOS
bool
oct_to_env (char *envar, unsigned long num)
{
char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
-
+
snprintf (buf, sizeof buf, "0%lo", num);
setenv (envar, buf, 1);
}
dec_to_env ("TAR_CTIME", st->stat.st_ctime);
dec_to_env ("TAR_SIZE", st->stat.st_size);
dec_to_env ("TAR_UID", st->stat.st_uid);
- dec_to_env ("TAR_GID", st->stat.st_gid);
+ dec_to_env ("TAR_GID", st->stat.st_gid);
switch (type)
{
{
int p[2];
char *argv[4];
-
+
xpipe (p);
pipe_handler = signal (SIGPIPE, SIG_IGN);
pid = xfork ();
/* Child */
xdup2 (p[PREAD], STDIN_FILENO);
xclose (p[PWRITE]);
-
+
stat_to_env (file_name, typechar, st);
argv[0] = "/bin/sh";
sys_wait_command (void)
{
int status;
-
+
if (pid < 0)
return;
}
#endif /* not MSDOS */
-
{
int c;
size_t counter = 0;
-
+
for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp))
{
if (c == 0)
size_t new_argc;
bool is_stdin = false;
enum read_file_list_state read_state;
-
+
if (!strcmp (filename, "-"))
{
is_stdin = true;
if (read_state == file_list_zero)
{
size_t size;
-
+
WARN ((0, 0, N_("%s: file name read contains nul character"),
quotearg_colon (filename)));
for (; size > 0; size--, p++)
if (*p)
obstack_1grow (&argv_stk, *p);
- else
+ else
obstack_1grow (&argv_stk, '\n');
obstack_1grow (&argv_stk, 0);
count = 1;
stat_error (arg);
USAGE_ERROR ((0, 0, _("Date sample file not found")));
}
- newer_mtime_option.tv_sec = st.st_mtime;
- newer_mtime_option.tv_nsec = TIMESPEC_NS (st.st_mtim);
+ newer_mtime_option = get_stat_mtime (&st);
}
else
{
if (! get_date (&newer_mtime_option, arg, NULL))
{
WARN ((0, 0, _("Substituting %s for unknown date format %s"),
- tartime (newer_mtime_option.tv_sec), quote (arg)));
+ tartime (newer_mtime_option, false), quote (arg)));
newer_mtime_option.tv_nsec = 0;
}
else
if (verbose_option && args.textual_date_option)
{
- /* FIXME: tartime should support nanoseconds, too, so that this
- comparison doesn't complain about lost nanoseconds. */
- char const *treated_as = tartime (newer_mtime_option.tv_sec);
+ char const *treated_as = tartime (newer_mtime_option, true);
if (strcmp (args.textual_date_option, treated_as) != 0)
- WARN ((0, 0,
- ngettext ("Treating date `%s' as %s + %ld nanosecond",
- "Treating date `%s' as %s + %ld nanoseconds",
- newer_mtime_option.tv_nsec),
- args.textual_date_option, treated_as,
- newer_mtime_option.tv_nsec));
+ WARN ((0, 0, _("Treating date `%s' as %s"),
+ args.textual_date_option, treated_as));
}
}
trailing slash before it was normalized. */
char *link_name; /* name of link for the current archive entry. */
- unsigned int devminor; /* device minor number */
- unsigned int devmajor; /* device major number */
char *uname; /* user name of owner */
char *gname; /* group name of owner */
struct stat stat; /* regular filesystem stat */
- /* Nanosecond parts of file timestamps (if available) */
- unsigned long atime_nsec;
- unsigned long mtime_nsec;
- unsigned long ctime_nsec;
-
off_t archive_file_size; /* Size of file as stored in the archive.
Equals stat.st_size for non-sparse files */
#include <fnmatch.h>
#include <hash.h>
+#include <inttostr.h>
#include <quotearg.h>
#include <stpcpy.h>
-#include <xstrtol.h>
#include "common.h"
#include <fnmatch.h>
+#if !HAVE_DECL_STRTOIMAX && !defined strtoimax
+intmax_t strtoimax ();
+#endif
+#if !HAVE_DECL_STRTOUMAX && !defined strtoumax
+uintmax_t strtoumax ();
+#endif
+
static bool xheader_protected_pattern_p (char const *pattern);
static bool xheader_protected_keyword_p (char const *keyword);
static void xheader_set_single_keyword (char *) __attribute__ ((noreturn));
However it should wait until buffer.c is finally rewritten */
+enum { BILLION = 1000000000, LOG10_BILLION = 9 };
+
\f
/* Keyword options */
}
}
-static void
-to_decimal (uintmax_t value, char *where, size_t size)
-{
- size_t i = 0, j;
-
- where[i++] = 0;
- do
- {
- where[i++] = '0' + value % 10;
- value /= 10;
- }
- while (i < size && value);
- for (j = 0, i--; j < i; j++, i--)
- {
- char c = where[j];
- where[j] = where[i];
- where[i] = c;
- }
-}
-
/*
string Includes: Replaced By:
%d The directory name of the file,
const char *p;
char *dir = NULL;
char *base = NULL;
- char pidbuf[64];
- char nbuf[64];
+ char pidbuf[UINTMAX_STRSIZE_BOUND];
+ char const *pptr;
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ char const *nptr = NULL;
for (p = fmt; *p && (p = strchr (p, '%')); )
{
case 'd':
if (st)
{
- dir = safer_name_suffix (dir_name (st->orig_file_name),
+ dir = safer_name_suffix (dir_name (st->orig_file_name),
false, absolute_names_option);
len += strlen (dir) - 1;
}
break;
case 'p':
- to_decimal (getpid (), pidbuf, sizeof pidbuf);
- len += strlen (pidbuf) - 1;
+ pptr = umaxtostr (getpid (), pidbuf);
+ len += pidbuf + sizeof pidbuf - 1 - pptr - 1;
break;
case 'n':
if (allow_n)
{
- to_decimal (global_header_count + 1, pidbuf, sizeof pidbuf);
- len += strlen (nbuf) - 1;
+ nptr = umaxtostr (global_header_count + 1, nbuf);
+ len += nbuf + sizeof nbuf - 1 - nptr - 1;
}
break;
}
break;
case 'p':
- q = stpcpy (q, pidbuf);
+ q = stpcpy (q, pptr);
p += 2;
break;
case 'n':
- if (allow_n)
+ if (nptr)
{
- q = stpcpy (q, nbuf);
+ q = stpcpy (q, nptr);
p += 2;
}
/* else fall through */
errno = 0;
len = strtoul (p, &len_lim, 10);
- if (len_max < len || (len == ULONG_MAX && errno == ERANGE))
+ if (len_max < len)
{
- ERROR ((0, 0, _("Malformed extended header: length out of range")));
+ int len_len = len_lim - p;
+ ERROR ((0, 0, _("Extended header length %*s is out of range"),
+ len_len, p));
return false;
}
while (size > 0);
}
-static size_t
-format_uintmax (uintmax_t val, char *buf, size_t s)
-{
- if (!buf)
- {
- s = 0;
- do
- s++;
- while ((val /= 10) != 0);
- }
- else
- {
- char *p = buf + s - 1;
-
- do
- {
- *p-- = val % 10 + '0';
- }
- while ((val /= 10) != 0);
-
- while (p >= buf)
- *p-- = '0';
- }
- return s;
-}
-
static void
xheader_print (struct xheader *xhdr, char const *keyword, char const *value)
{
size_t len = strlen (keyword) + strlen (value) + 3; /* ' ' + '=' + '\n' */
- size_t p, n = 0;
- char nbuf[100];
+ size_t p;
+ size_t n = 0;
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ char const *np;
do
{
p = n;
- n = format_uintmax (len + p, NULL, 0);
+ np = umaxtostr (len + p, nbuf);
+ n = nbuf + sizeof nbuf - 1 - np;
}
while (n != p);
- format_uintmax (len + n, nbuf, n);
- obstack_grow (xhdr->stk, nbuf, n);
+ obstack_grow (xhdr->stk, np, n);
obstack_1grow (xhdr->stk, ' ');
obstack_grow (xhdr->stk, keyword, strlen (keyword));
obstack_1grow (xhdr->stk, '=');
\f
/* Implementations */
+
+static void
+out_of_range_header (char const *keyword, char const *value,
+ uintmax_t minus_minval, uintmax_t maxval)
+{
+ char minval_buf[UINTMAX_STRSIZE_BOUND + 1];
+ char maxval_buf[UINTMAX_STRSIZE_BOUND];
+ char *minval_string = umaxtostr (minus_minval, minval_buf + 1);
+ char *maxval_string = umaxtostr (maxval, maxval_buf);
+ if (minus_minval)
+ *--minval_string = '-';
+
+ /* TRANSLATORS: The first %s is the pax extended header keyword
+ (atime, gid, etc.). */
+ ERROR ((0, 0, _("Extended header %s=%s is out of range %s..%s"),
+ keyword, value, minval_string, maxval_string));
+}
+
static void
code_string (char const *string, char const *keyword, struct xheader *xhdr)
{
}
static void
-code_time (time_t t, unsigned long nano,
- char const *keyword, struct xheader *xhdr)
+code_time (struct timespec t, char const *keyword, struct xheader *xhdr)
{
- char sbuf[200];
- size_t s = format_uintmax (t, NULL, 0);
- if (s + 11 >= sizeof sbuf)
- return;
- format_uintmax (t, sbuf, s);
- sbuf[s++] = '.';
- s += format_uintmax (nano, sbuf + s, 9);
- sbuf[s] = 0;
- xheader_print (xhdr, keyword, sbuf);
+ time_t s = t.tv_sec;
+ int ns = t.tv_nsec;
+ char sbuf[1/*"-"*/ + UINTMAX_STRSIZE_BOUND + 1/*"."*/ + LOG10_BILLION];
+ char *np;
+ bool negative = s < 0;
+
+ if (negative && ns != 0)
+ {
+ s++;
+ ns = BILLION - ns;
+ }
+
+ np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
+ if (negative)
+ *--np = '-';
+ code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
+ xheader_print (xhdr, keyword, np);
}
-static void
-decode_time (char const *arg, time_t *secs, unsigned long *nsecs)
+static bool
+decode_time (struct timespec *ts, char const *arg, char const *keyword)
{
- uintmax_t u;
+ time_t s;
+ unsigned long int ns = 0;
char *p;
- if (xstrtoumax (arg, &p, 10, &u, "") == LONGINT_OK)
+ char *arg_lim;
+ bool negative = *arg == '-';
+
+ errno = 0;
+
+ if (ISDIGIT (arg[negative]))
{
- *secs = u;
- if (*p == '.' && xstrtoumax (p+1, NULL, 10, &u, "") == LONGINT_OK)
- *nsecs = u;
+ if (negative)
+ {
+ intmax_t i = strtoimax (arg, &arg_lim, 10);
+ if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0)
+ goto out_of_range;
+ s = i;
+ }
+ else
+ {
+ uintmax_t i = strtoumax (arg, &arg_lim, 10);
+ if (TYPE_MAXIMUM (time_t) < i)
+ goto out_of_range;
+ s = i;
+ }
+
+ p = arg_lim;
+
+ if (errno == ERANGE)
+ goto out_of_range;
+
+ if (*p == '.')
+ {
+ int digits = 0;
+ bool trailing_nonzero = false;
+
+ while (ISDIGIT (*++p))
+ if (digits < LOG10_BILLION)
+ {
+ ns = 10 * ns + (*p - '0');
+ digits++;
+ }
+ else
+ trailing_nonzero |= *p != '0';
+
+ while (digits++ < LOG10_BILLION)
+ ns *= 10;
+
+ if (negative)
+ {
+ /* Convert "-1.10000000000001" to s == -2, ns == 89999999.
+ I.e., truncate time stamps towards minus infinity while
+ converting them to internal form. */
+ ns += trailing_nonzero;
+ if (ns != 0)
+ {
+ if (s == TYPE_MINIMUM (time_t))
+ goto out_of_range;
+ s--;
+ ns = BILLION - ns;
+ }
+ }
+ }
+
+ if (! *p)
+ {
+ ts->tv_sec = s;
+ ts->tv_nsec = ns;
+ return true;
+ }
}
+
+ ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
+ keyword, arg));
+ return false;
+
+ out_of_range:
+ out_of_range_header (keyword, arg, - (uintmax_t) TYPE_MINIMUM (time_t),
+ TYPE_MAXIMUM (time_t));
+ return false;
}
static void
code_num (uintmax_t value, char const *keyword, struct xheader *xhdr)
{
- char sbuf[100];
- size_t s = format_uintmax (value, NULL, 0);
- format_uintmax (value, sbuf, s);
- sbuf[s] = 0;
- xheader_print (xhdr, keyword, sbuf);
+ char sbuf[UINTMAX_STRSIZE_BOUND];
+ xheader_print (xhdr, keyword, umaxtostr (value, sbuf));
+}
+
+static bool
+decode_num (uintmax_t *num, char const *arg, uintmax_t maxval,
+ char const *keyword)
+{
+ uintmax_t u;
+ char *arg_lim;
+
+ if (! (ISDIGIT (*arg)
+ && (errno = 0, u = strtoumax (arg, &arg_lim, 10), !*arg_lim)))
+ {
+ ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
+ keyword, arg));
+ return false;
+ }
+
+ if (! (u <= maxval && errno != ERANGE))
+ {
+ out_of_range_header (keyword, arg, 0, maxval);
+ return false;
+ }
+
+ *num = u;
+ return true;
}
static void
atime_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void *data __attribute__ ((unused)))
{
- code_time (st->stat.st_atime, st->atime_nsec, keyword, xhdr);
+ code_time (get_stat_atime (&st->stat), keyword, xhdr);
}
static void
atime_decoder (struct tar_stat_info *st, char const *arg)
{
- decode_time (arg, &st->stat.st_atime, &st->atime_nsec);
+ struct timespec ts;
+ if (decode_time (&ts, arg, "atime"))
+ set_stat_atime (&st->stat, ts);
}
static void
gid_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), "gid"))
st->stat.st_gid = u;
}
ctime_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void *data __attribute__ ((unused)))
{
- code_time (st->stat.st_ctime, st->ctime_nsec, keyword, xhdr);
+ code_time (get_stat_ctime (&st->stat), keyword, xhdr);
}
static void
ctime_decoder (struct tar_stat_info *st, char const *arg)
{
- decode_time (arg, &st->stat.st_ctime, &st->ctime_nsec);
+ struct timespec ts;
+ if (decode_time (&ts, arg, "ctime"))
+ set_stat_ctime (&st->stat, ts);
}
static void
mtime_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void *data __attribute__ ((unused)))
{
- code_time (st->stat.st_mtime, st->mtime_nsec, keyword, xhdr);
+ code_time (get_stat_mtime (&st->stat), keyword, xhdr);
}
static void
mtime_decoder (struct tar_stat_info *st, char const *arg)
{
- decode_time (arg, &st->stat.st_mtime, &st->mtime_nsec);
+ struct timespec ts;
+ if (decode_time (&ts, arg, "mtime"))
+ set_stat_mtime (&st->stat, ts);
}
static void
size_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "size"))
st->archive_file_size = st->stat.st_size = u;
}
uid_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), "uid"))
st->stat.st_uid = u;
}
sparse_size_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.size"))
st->stat.st_size = u;
}
sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numblocks"))
{
st->sparse_map_size = u;
- st->sparse_map = calloc(st->sparse_map_size, sizeof(st->sparse_map[0]));
+ st->sparse_map = xcalloc (u, sizeof st->sparse_map[0]);
st->sparse_map_avail = 0;
}
}
sparse_offset_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.offset"))
st->sparse_map[st->sparse_map_avail].offset = u;
}
sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg)
{
uintmax_t u;
- if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
+ if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numbytes"))
{
if (st->sparse_map_avail == st->sparse_map_size)
- {
- st->sparse_map_size *= 2;
- st->sparse_map = xrealloc (st->sparse_map,
- st->sparse_map_size
- * sizeof st->sparse_map[0]);
- }
+ st->sparse_map = x2nrealloc (st->sparse_map,
+ &st->sparse_map_size,
+ sizeof st->sparse_map[0]);
st->sparse_map[st->sparse_map_avail++].numbytes = u;
}
}