You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Place - Suite 330, Boston, MA 02111-1307, USA. */
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "system.h"
\f
/*------------------------------------------------------------------------.
-| Convert VALUE into a size-SIZE field at WHERE, including a |
+| Convert VALUE (with substitute SUBSTITUTE if VALUE is out of range) |
+| into a size-SIZE field at WHERE, including a |
| trailing space. For example, 3 for SIZE means two digits and a space. |
| |
| We assume the trailing NUL is already there and don't fill it in. This |
| fact is used by start_header and finish_header, so don't change it! |
`------------------------------------------------------------------------*/
-/* This should be equivalent to: sprintf (WHERE, "%*lo ", SIZE - 1, VALUE);
- except that we don't assume VALUE fits in an unsigned long, and
- except that sprintf fills in the trailing NUL and we don't. */
+/* Output VALUE in octal, using SUBSTITUTE if value won't fit.
+ Output to buffer WHERE with size SIZE.
+ TYPE is the kind of value being output (useful for diagnostics).
+ Prefer SIZE - 1 octal digits (with leading '0's), followed by '\0';
+ but if SIZE octal digits would fit, omit the '\0'. */
static void
-to_oct (uintmax_t value, char *where, size_t size, const char *type)
+to_oct (uintmax_t value, uintmax_t substitute, char *where, size_t size, const char *type)
{
uintmax_t v = value;
size_t i = size;
- where[--i] = ' '; /* put in the space, though */
+
+# define MAX_OCTAL_VAL_WITH_DIGITS(digits) \
+ ((digits) * 3 < sizeof (uintmax_t) * CHAR_BIT \
+ ? ((uintmax_t) 1 << ((digits) * 3)) - 1 \
+ : (uintmax_t) -1)
+
+ /* Output a trailing NUL unless the value is too large. */
+ if (value <= MAX_OCTAL_VAL_WITH_DIGITS (size - 1))
+ where[--i] = '\0';
/* Produce the digits -- at least one. */
}
while (i != 0 && v != 0);
- /* Leading spaces, if necessary. */
+ /* Leading zeros, if necessary. */
while (i != 0)
- where[--i] = ' ';
+ where[--i] = '0';
if (v != 0)
{
- char buf[UINTMAX_STRSIZE_BOUND];
- ERROR ((0, 0, _("%s value %s is too large to fit in a %u-bit field"),
- type, STRINGIFY_BIGINT (value, buf),
- (unsigned) ((size - 1) * 3)));
+ uintmax_t maxval = MAX_OCTAL_VAL_WITH_DIGITS (size);
+ char buf1[UINTMAX_STRSIZE_BOUND];
+ char buf2[UINTMAX_STRSIZE_BOUND];
+ char buf3[UINTMAX_STRSIZE_BOUND];
+ char *value_string = STRINGIFY_BIGINT (value, buf1);
+ char *maxval_string = STRINGIFY_BIGINT (maxval, buf2);
+ if (substitute)
+ {
+ substitute &= maxval;
+ WARN ((0, 0, _("%s value %s too large (max=%s); substituting %s"),
+ type, value_string, maxval_string,
+ STRINGIFY_BIGINT (substitute, buf3)));
+ to_oct (substitute, (uintmax_t) 0, where, size, type);
+ }
+ else
+ ERROR ((0, 0, _("%s value %s too large (max=%s)"),
+ type, value_string, maxval_string));
}
}
+#ifndef GID_NOBODY
+#define GID_NOBODY 0
+#endif
void
gid_to_oct (gid_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "gid_t");
+ to_oct ((uintmax_t) v, (uintmax_t) GID_NOBODY, p, s, "gid_t");
}
void
major_to_oct (major_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "major_t");
+ to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "major_t");
}
void
minor_to_oct (minor_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "minor_t");
+ to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "minor_t");
}
void
mode_to_oct (mode_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "mode_t");
+ /* In the common case where the internal and external mode bits are the same,
+ propagate all unknown bits to the external mode.
+ This matches historical practice.
+ Otherwise, just copy the bits we know about. */
+ uintmax_t u =
+ ((S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX
+ && S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC
+ && S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC
+ && S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC)
+ ? v
+ : ((v & S_ISUID ? TSUID : 0)
+ | (v & S_ISGID ? TSGID : 0)
+ | (v & S_ISVTX ? TSVTX : 0)
+ | (v & S_IRUSR ? TUREAD : 0)
+ | (v & S_IWUSR ? TUWRITE : 0)
+ | (v & S_IXUSR ? TUEXEC : 0)
+ | (v & S_IRGRP ? TGREAD : 0)
+ | (v & S_IWGRP ? TGWRITE : 0)
+ | (v & S_IXGRP ? TGEXEC : 0)
+ | (v & S_IROTH ? TOREAD : 0)
+ | (v & S_IWOTH ? TOWRITE : 0)
+ | (v & S_IXOTH ? TOEXEC : 0)));
+ to_oct (u, (uintmax_t) 0, p, s, "mode_t");
}
void
off_to_oct (off_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "off_t");
+ to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "off_t");
}
void
size_to_oct (size_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "size_t");
+ to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "size_t");
}
void
time_to_oct (time_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "time_t");
+ to_oct ((uintmax_t) v, (uintmax_t) 0, p, s, "time_t");
}
+#ifndef UID_NOBODY
+#define UID_NOBODY 0
+#endif
void
uid_to_oct (uid_t v, char *p, size_t s)
{
- to_oct ((uintmax_t) v, p, s, "uid_t");
+ to_oct ((uintmax_t) v, (uintmax_t) UID_NOBODY, p, s, "uid_t");
}
void
uintmax_to_oct (uintmax_t v, char *p, size_t s)
{
- to_oct (v, p, s, "uintmax_t");
+ to_oct (v, (uintmax_t) 0, p, s, "uintmax_t");
}
\f
/* Writing routines. */
acceptor for Paul's test. */
if (archive_format == V7_FORMAT)
- MODE_TO_OCT (st->st_mode & 07777, header->header.mode);
+ MODE_TO_OCT (st->st_mode & MODE_ALL, header->header.mode);
else
MODE_TO_OCT (st->st_mode, header->header.mode);
/* Fill in the checksum field. It's formatted differently from the
other fields: it has [6] digits, a null, then a space -- rather than
- digits, a space, then a null. We use to_oct then write the null in
- over to_oct's space. The final space is already there, from
+ digits, then a null. We use to_oct.
+ The final space is already there, from
checksumming, and to_oct doesn't modify it.
This is a fast way to do:
sprintf(header->header.chksum, "%6o", sum); */
- UINTMAX_TO_OCT ((uintmax_t) sum, header->header.chksum);
- header->header.chksum[6] = '\0'; /* zap the space */
+ uintmax_to_oct ((uintmax_t) sum, header->header.chksum, 7);
set_next_block_after (header);
init_sparsearray ();
clear_buffer (buffer);
- while (count = read (file, buffer, sizeof buffer), count != 0)
+ while (count = safe_read (file, buffer, sizeof buffer), count != 0)
{
/* Realloc the scratch area as necessary. FIXME: should reallocate
only at beginning of a new instance of non-zero data. */
break;
}
- if (lseek (file, sparsearray[sparse_index++].offset, 0) < 0)
+ if (lseek (file, sparsearray[sparse_index++].offset, SEEK_SET) < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
ERROR ((0, errno, _("lseek error at byte %s in file %s"),
#if 0
if (amount_read)
{
- count = read (file, start->buffer + amount_read,
- BLOCKSIZE - amount_read);
+ count = safe_read (file, start->buffer + amount_read,
+ BLOCKSIZE - amount_read);
bufsize -= BLOCKSIZE - amount_read;
amount_read = 0;
set_next_block_after (start);
#endif
/* Store the data. */
- count = read (file, start->buffer, BLOCKSIZE);
+ count = safe_read (file, start->buffer, BLOCKSIZE);
if (count < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
char buffer[BLOCKSIZE];
clear_buffer (buffer);
- count = read (file, buffer, bufsize);
+ count = safe_read (file, buffer, bufsize);
memcpy (start->buffer, buffer, BLOCKSIZE);
}
union block *exhdr;
char save_typeflag;
struct utimbuf restore_times;
+ off_t restore_size;
/* FIXME: `header' and `upperbound' might be used uninitialized in this
function. Reported by Bruno Haible. */
Otherwise, use lstat (which falls back to stat if no symbolic links). */
if (dereference_option != 0
-#ifdef STX_HIDDEN /* AIX */
+#if STX_HIDDEN && !_LARGE_FILES /* AIX */
? statx (p, ¤t_stat, STATSIZE, STX_HIDDEN)
: statx (p, ¤t_stat, STATSIZE, STX_HIDDEN | STX_LINK)
#else
restore_times.actime = current_stat.st_atime;
restore_times.modtime = current_stat.st_mtime;
+ restore_size = current_stat.st_size;
#ifdef S_ISHIDDEN
if (S_ISHIDDEN (current_stat.st_mode))
files when archive is meant for /dev/null. */
if (dev_null_output
- || (sizeleft == 0 && 0444 == (0444 & current_stat.st_mode)))
+ || (sizeleft == 0
+ && MODE_R == (MODE_R & current_stat.st_mode)))
f = -1;
else
{
}
if (save_typeflag == GNUTYPE_SPARSE)
{
- if (finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
+ if (f < 0
+ || finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
goto padit;
}
else
if (f < 0)
count = bufsize;
else
- count = read (f, start->buffer, bufsize);
+ count = safe_read (f, start->buffer, bufsize);
if (count < 0)
{
char buf[UINTMAX_STRSIZE_BOUND];
if (f >= 0)
{
- close (f);
+ struct stat final_stat;
+ if (fstat (f, &final_stat) != 0)
+ ERROR ((0, errno, "%s: fstat", p));
+ else if (final_stat.st_mtime != restore_times.modtime
+ || final_stat.st_size != restore_size)
+ ERROR ((0, errno, _("%s: file changed as we read it"), p));
+ if (close (f) != 0)
+ ERROR ((0, errno, _("%s: close"), p));
if (atime_preserve_option)
utime (p, &restore_times);
}
while (entry = readdir (directory), entry)
{
- /* Skip `.' and `..'. */
+ /* Skip `.', `..', and excluded file names. */
- if (is_dot_or_dotdot (entry->d_name))
+ if (is_dot_or_dotdot (entry->d_name)
+ || excluded_filename (excluded, entry->d_name))
continue;
if ((int) NAMLEN (entry) + len >= buflen)
#endif
}
strcpy (namebuf + len, entry->d_name);
- if (exclude_option && check_exclude (namebuf))
- continue;
dump_file (namebuf, our_device, 0);
}