From 2947023d277cb0a787c73721d6190a75444cd65f Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 4 Oct 2009 23:37:57 +0300 Subject: [PATCH] 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. --- src/common.h | 5 +++-- src/create.c | 3 +-- src/list.c | 39 ++++++++++++++++++++++----------------- tests/append02.at | 26 ++++++++++++++++++++------ 4 files changed, 46 insertions(+), 27 deletions(-) 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 diff --git a/tests/append02.at b/tests/append02.at index 0986e51..7b8e07d 100644 --- a/tests/append02.at +++ b/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: -# <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] +# 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. -- 2.45.2