Quellcode durchsuchen

Fix bug in OLDGNU format creation.

See tests/append02.at for a detailed description

* src/common.h (MODE_FROM_HEADER): Take additional argument.
(mode_from_header): Likewise.
* src/create.c (mode_to_chars): Store all mode bits if
using OLDGNU_FORMAT. This reverses f4e4adea80a.
* src/list.c (decode_header): Use header mode field
to discern between GNU and OLDGNU formats.
(mode_from_header): Store unrecognized mode bits (from 10th up)
in the location pointed to by the third parameter.
* tests/append02.at: Update documentation and references.
Sergey Poznyakoff vor 15 Jahren
Ursprung
Commit
2947023d27
4 geänderte Dateien mit 46 neuen und 27 gelöschten Zeilen
  1. 3 2
      src/common.h
  2. 1 2
      src/create.c
  3. 22 17
      src/list.c
  4. 20 6
      tests/append02.at

+ 3 - 2
src/common.h

@@ -557,7 +557,8 @@ char const *tartime (struct timespec t, bool full_time);
 #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))
@@ -567,7 +568,7 @@ char const *tartime (struct timespec t, bool full_time);
 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);

+ 1 - 2
src/create.c

@@ -402,8 +402,7 @@ mode_to_chars (mode_t v, char *p, size_t s)
       && 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;

+ 22 - 17
src/list.c

@@ -531,7 +531,9 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
 	       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
@@ -546,12 +548,12 @@ decode_header (union block *header, struct tar_stat_info *stat_info,
 	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,
@@ -885,25 +887,28 @@ minor_from_header (const char *p, size_t s)
 		      (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

+ 20 - 6
tests/append02.at

@@ -1,7 +1,7 @@
 # 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
@@ -26,13 +26,27 @@
 #      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:
-#   <[email protected]>
-#   http://lists.gnu.org/archive/html/bug-tar/2006-07/msg00029.html
+# [1] <[email protected]>
+#     http://lists.gnu.org/archive/html/bug-tar/2006-07/msg00029.html
+# [2] <[email protected]>
+#     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.