oldgnu file format changes (file permissions field).

View: New views
2 Messages — Rating Filter:   Alert me  

oldgnu file format changes (file permissions field).

by Igor Zhbanov :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello!

I have found that tar´s after 1.16 creates binary different archives
in oldgnu format.
The difference is in file mode field.

To test this I use the command: tar cvf test.tar -H oldgnu tar.c

tar-1.15.91 sets mode field to 0100644 (as all previous versions),
but tar-1.16 (and all later versions) sets the mode field to 0000644
in the same oldgnu file format.

So different versions of tar create binary different files with
different checksums. That's sad. :-(

That difference is because of changes to function mode_to_chars in src/create.c.

Old version was:

bool
mode_to_chars (mode_t v, char *p, size_t s)
{
  /* In the common case where the internal and external mode bits are the same,
     and we are not using POSIX or GNU format,
     propagate all unknown bits to the external mode.
     This matches historical practice.
     Otherwise, just copy the bits we know about.  */
  int negative;
  uintmax_t u;
  if (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
      && archive_format != POSIX_FORMAT
      && archive_format != USTAR_FORMAT
      && archive_format != GNU_FORMAT)
    {
      negative = v < 0;
      u = v;
    }
  else
    {
      negative = 0;
      u = ((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));
    }
  return to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
}

And the new is:

bool
mode_to_chars (mode_t v, char *p, size_t s)
{
  /* In the common case where the internal and external mode bits are the same,
     and we are not using POSIX or GNU format,
     propagate all unknown bits to the external mode.
     This matches historical practice.
     Otherwise, just copy the bits we know about.  */
  int negative;
  uintmax_t u;
  if (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
      && archive_format != POSIX_FORMAT
      && archive_format != USTAR_FORMAT
      && archive_format != GNU_FORMAT
      && archive_format != OLDGNU_FORMAT)
    {
      negative = v < 0;
      u = v;
    }
  else
    {
      negative = 0;
      u = ((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));
    }
  return to_chars (negative, u, sizeof v, 0, p, s, "mode_t");
}

The difference is in IF statement:
...
      && archive_format != POSIX_FORMAT
      && archive_format != USTAR_FORMAT
      && archive_format != GNU_FORMAT)
vs.
...
      && archive_format != POSIX_FORMAT
      && archive_format != USTAR_FORMAT
      && archive_format != GNU_FORMAT
      && archive_format != OLDGNU_FORMAT)

So when different versions of tar create archive in oldgnu format,
different statements will be executed.

What do you think?



Re: oldgnu file format changes (file permissions field).

by Sergey Poznyakoff-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Igor Zhbanov <izh1979@...> ha escrit:

> Hello!

Hi,

> I have found that tar´s after 1.16 creates binary different archives
> in oldgnu format.
> The difference is in file mode field.

Thanks. Please try the attached patch.

Regards,
Sergey


diff --git a/src/common.h b/src/common.h
index 73865ec..1a6ca8b 100644
--- a/src/common.h
+++ b/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);
diff --git a/src/create.c b/src/create.c
index 6f3113e..79c80ce 100644
--- a/src/create.c
+++ b/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;
diff --git a/src/list.c b/src/list.c
index 2436901..bba430a 100644
--- a/src/list.c
+++ b/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