#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where))
#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where))
#define MINOR_FROM_HEADER(where) minor_from_header (where, sizeof (where))
-#define MODE_FROM_HEADER(where) mode_from_header (where, sizeof (where))
+#define MODE_FROM_HEADER(where, hbits) \
+ mode_from_header (where, sizeof (where), hbits)
#define OFF_FROM_HEADER(where) off_from_header (where, sizeof (where))
#define SIZE_FROM_HEADER(where) size_from_header (where, sizeof (where))
#define TIME_FROM_HEADER(where) time_from_header (where, sizeof (where))
gid_t gid_from_header (const char *buf, size_t size);
major_t major_from_header (const char *buf, size_t size);
minor_t minor_from_header (const char *buf, size_t size);
-mode_t mode_from_header (const char *buf, size_t size);
+mode_t mode_from_header (const char *buf, size_t size, unsigned *hbits);
off_t off_from_header (const char *buf, size_t size);
size_t size_from_header (const char *buf, size_t size);
time_t time_from_header (const char *buf, size_t size);
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC
&& archive_format != POSIX_FORMAT
&& archive_format != USTAR_FORMAT
- && archive_format != GNU_FORMAT
- && archive_format != OLDGNU_FORMAT)
+ && archive_format != GNU_FORMAT)
{
negative = v < 0;
u = v;
enum archive_format *format_pointer, int do_user_group)
{
enum archive_format format;
-
+ unsigned hbits; /* high bits of the file mode. */
+ mode_t mode = MODE_FROM_HEADER (header->header.mode, &hbits);
+
if (strcmp (header->header.magic, TMAGIC) == 0)
{
if (header->star_header.prefix[130] == 0
format = USTAR_FORMAT;
}
else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
- format = OLDGNU_FORMAT;
+ format = hbits ? OLDGNU_FORMAT : GNU_FORMAT;
else
format = V7_FORMAT;
*format_pointer = format;
- stat_info->stat.st_mode = MODE_FROM_HEADER (header->header.mode);
+ stat_info->stat.st_mode = mode;
stat_info->mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
stat_info->mtime.tv_nsec = 0;
assign_string (&stat_info->uname,
(uintmax_t) TYPE_MAXIMUM (minor_t), false, false);
}
+/* Convert P to the file mode, as understood by tar.
+ Store unrecognized mode bits (from 10th up) in HBITS. */
mode_t
-mode_from_header (const char *p, size_t s)
+mode_from_header (const char *p, size_t s, unsigned *hbits)
{
- /* Do not complain about unrecognized mode bits. */
unsigned u = from_header (p, s, "mode_t",
- (uintmax_t) TYPE_MINIMUM (mode_t),
TYPE_MAXIMUM (uintmax_t), false, false);
- return ((u & TSUID ? S_ISUID : 0)
- | (u & TSGID ? S_ISGID : 0)
- | (u & TSVTX ? S_ISVTX : 0)
- | (u & TUREAD ? S_IRUSR : 0)
- | (u & TUWRITE ? S_IWUSR : 0)
- | (u & TUEXEC ? S_IXUSR : 0)
- | (u & TGREAD ? S_IRGRP : 0)
- | (u & TGWRITE ? S_IWGRP : 0)
- | (u & TGEXEC ? S_IXGRP : 0)
- | (u & TOREAD ? S_IROTH : 0)
- | (u & TOWRITE ? S_IWOTH : 0)
- | (u & TOEXEC ? S_IXOTH : 0));
+ mode_t mode = ((u & TSUID ? S_ISUID : 0)
+ | (u & TSGID ? S_ISGID : 0)
+ | (u & TSVTX ? S_ISVTX : 0)
+ | (u & TUREAD ? S_IRUSR : 0)
+ | (u & TUWRITE ? S_IWUSR : 0)
+ | (u & TUEXEC ? S_IXUSR : 0)
+ | (u & TGREAD ? S_IRGRP : 0)
+ | (u & TGWRITE ? S_IWGRP : 0)
+ | (u & TGEXEC ? S_IXGRP : 0)
+ | (u & TOREAD ? S_IROTH : 0)
+ | (u & TOWRITE ? S_IWOTH : 0)
+ | (u & TOEXEC ? S_IXOTH : 0));
+ *hbits = mode ^ u;
+ return mode;
}
off_t
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
-# Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+# Copyright (C) 2006, 2007, 2009 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
# tar rf archive file1
# tar rt archive file2
#
-# produced different archives (GNU format is assumed). Namely, in the
-# second case the mode field of all members, except the first, was truncated
-# to lower 3 octets (& 0777).
+# produced different archives (GNU format is assumed). It was reported
+# by TAMUKI Shoichi on 2006-07-21 [1].
+#
+# The bug was due to tar being unable to discern between GNU and OLDGNU
+# formats and always assuming the latter. The main difference between
+# the two is that OLDGNU preserves all bits in the mode field, whereas
+# GNU format keeps only the lower 9 ones (mode & 0777).
+#
+# This was fixed on 2006-07-24 (commit f4e4adea80a) by making tar truncate
+# the mode field even in OLDGNU format. Obviously, the fix broke the
+# format backward compatibility, but it went unnoticed until 2009-10-03
+# (after all, the OLDGNU format is not in much use nowadays), when
+# Igor Zhbanov reported it [2].
+#
+# The final fix was applied on 2009-10-04.
#
# References:
-# <200607210526.AA03440@tamuki.linet.gr.jp>
-# http://lists.gnu.org/archive/html/bug-tar/2006-07/msg00029.html
+# [1] <200607210526.AA03440@tamuki.linet.gr.jp>
+# http://lists.gnu.org/archive/html/bug-tar/2006-07/msg00029.html
+# [2] <f44001920910020335v4cadfesf54f6593d5124814@mail.gmail.com>
+# http://lists.gnu.org/archive/html/bug-tar/2009-10/msg00006.html
# The test case below verifies that the equivalent create and append commands
# produce binary equivalent archives for all formats.