Compare commits

..

No commits in common. "master" and "v1.35" have entirely different histories.

339 changed files with 6679 additions and 7340 deletions

4
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "gnulib"]
path = gnulib
url = https://git.savannah.gnu.org/git/gnulib.git
url = git://git.sv.gnu.org/gnulib.git
[submodule "paxutils"]
path = paxutils
url = https://git.savannah.gnu.org/git/paxutils.git
url = git://git.sv.gnu.org/paxutils.git

View File

@ -2,7 +2,7 @@ Currently there is just one ChangeLog file for tar, but
there used to be separate ChangeLog files for each subdirectory.
This file records what used to be in those separate files.
Copyright 1989-1997, 2013, 2023-2026 Free Software Foundation, Inc.
Copyright 1989-1997, 2013, 2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -343,7 +343,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* configure.in: For mknod, also include <sys/types.h> prior to
<sys/stat.h>, as Ultrix needs this.
Reported by Bruce Jerrick, Bryant Fujimoto, Conrad Hughes, Erich
Stefan Boleyn, Jason R. Mastaler, Joshua R. Poulson, Jürgen Botz,
Stefan Boleyn, Jason R. Mastaler, Joshua R. Poulson, Jurgen Botz,
Serge Granik, Simon Wright, Ulrich Drepper and Vince Del Vecchio.
* configure.in: Replace execlp as needed (for Minix, mainly).
@ -1293,7 +1293,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* gmalloc.c: New, from elsewhere. This renames and updates
what was previously malloc.c. This also solves __const vs const.
* Makefile.in: Distribute gmalloc.c.
Reported by Cliff Krumvieda, François Pinard, Henrik Bakman,
Reported by Cliff Krumvieda, Francois Pinard, Henrik Bakman,
J.T. Conklin, Nelson H.F. Beebe and Tilman Schmidt.
1994-07-22 François Pinard <pinard@iro.umontreal.ca>
@ -2938,7 +2938,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Elmer Fittery, Eric Benson, Eric M. Boehm, Gerd Knorr, Graham
Whitted, Harald Milz, Heiko Schlichting, James V. Di Toro III,
Jan Carlson, Janne Snabb, Jeff Sorensen, Jens Henrik Jensen,
Jim Clausing, John J. Szetela, John R. Vanderpool, Jürgen Botz,
Jim Clausing, John J. Szetela, John R. Vanderpool, Jurgen Botz,
Karl Berry, Karlos Z. Smith, Karsten Thygesen, Koji Kishi,
Luke Mewburn, Manuel Munier, Marc Ewing, Matthew J. D'Errico,
Martin Goik, Maxime Taksar, maximum entropy, Michael Hayes,
@ -3851,7 +3851,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* tar.h: Merely define valloc as being malloc if valloc does
not exist.
* port.h: Remove valloc, which was only a dummy for malloc.
Reported by Cliff Krumvieda, François Pinard, Henrik Bakman,
Reported by Cliff Krumvieda, Francois Pinard, Henrik Bakman,
J.T. Conklin, Nelson H.F. Beebe and Tilman Schmidt.
1994-07-22 François Pinard <pinard@iro.umontreal.ca>
@ -5386,7 +5386,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* getdate.y : Parse European dates of the form YYMMDD.
In ftime(): Init timezone by calling localtime(), and remember that
timezone is in seconds, but we want timeb->timezone to be in minutes.
Reported by Jörgen Hägg.
Reported by Jörgen Haegg.
* rtape_lib.c (__rmt_open): Also look for /usr/bsd/rsh.
Declare signal handler as returning void instead of int if USG is

View File

@ -2,8 +2,8 @@ Currently the ChangeLog is generated automatically from the Git
revision history, but from 1997 to 2009 the ChangeLog file was
maintained by hand, under CVS. This file records the older log.
Copyright 1997-2001, 2003-2009, 2013, 2023-2026 Free Software
Foundation, Inc.
Copyright 1997-2001, 2003-2009, 2013, 2023 Free Software Foundation,
Inc.
This file is part of GNU tar.
@ -935,7 +935,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-10-02 Sergey Poznyakoff <gray@gnu.org.ua>
* THANKS: Add Jörg Weilbier
* THANKS: Add Joerg Weilbier
* src/buffer.c (new_volume): Initialize current_block
* src/xheader.c (xheader_string_end): Fix diagnostic message.
* tests/multiv05.at: New testcase.
@ -4523,7 +4523,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2003-11-12 Paul Eggert <eggert@twinsun.com>
Fix some C compatibility bugs reported by Jörg Schilling.
Fix some C compatibility bugs reported by Joerg Schilling.
* src/common.h (stripped_prefix_len): Fix misspelling
"stripped_path_len" in declaration.

View File

@ -56,3 +56,15 @@ s/inadvertantly/inadvertently/
4dfcd6c054a5e9e1a371c822a3be90564dd9b690
s/succesfully/successfully/

View File

@ -1,6 +1,6 @@
# Main Makefile for GNU tar.
# Copyright 1994-2026 Free Software Foundation, Inc.
# Copyright 1994-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

79
NEWS
View File

@ -1,78 +1,5 @@
GNU tar NEWS - User visible changes. 2026-01-30
GNU tar NEWS - User visible changes. 2023-07-18
Please send GNU tar bug reports to <bug-tar@gnu.org>
version 1.35.90 (git)
* New manual section "Reproducibility", for reproducible tarballs.
* New options: --set-mtime-command and --set-mtime-format
Both options are valid when archiving files.
** --set-mtime-command=COMMAND
For each FILE being archived, run "COMMAND FILE", parse its
output as time string and set mtime value of the archive member
from the result.
Unless --set-mtime-format is also used, the output is parsed
as argument to --mtime option (see GNU tar manual, chapter 4
"Date input formats".
** --set-mtime-format=FMT
Defines output format for the COMMAND set by the above option. If
used, command output will be parsed using strptime(3).
* Changes to behavior
** Skip file or archive member if transformed name is empty
If applying file name transformations (--transform and
--strip-component options) to a file or member name results in an
empty string that file or member is skipped and a warning is printed.
The warning can be suppressed using the --warning=empty-transform
option.
** --one-top-level=DIR now requires DIR to be relative.
Previously this restriction was alluded to in the manual but not enforced.
* Bug fixes
** When extracting, tar no longer follows symbolic links to targets
outside the working directory.
** Fixed O(n^2) time complexity bug for large numbers of directories when
extracting with --delay-directory-restore or reading incremental archives.
** tar no longer uses alloca, fixing an unlikely stack overflow.
** When diagnosing invalid extended headers tar now quotes control characters.
** Transformations that change case (e.g., --transform='s/.*/\L&/')
now work correctly with multi-byte characters.
** --no-overwrite-dir no longer changes permissions of existing directories,
not even temporarily. This matches the documentation better and avoids
some permissions glitches.
** tar no longer fails merely if an extraction directory is unreadable
on Linux kernels.
** tar now works better in strict debugging environments that do not
allow pointer arithmetic to escape from a sub-element of an array.
* Performance improvements
** Sparse files are now read and written with larger blocksizes.
** When extracting and neither --absolute-names (-P) nor --dereference
(-h) is used, tar no longer creates empty placeholder files
that are later replaced by symbolic links. The placeholders are no
longer needed now that tar by default no longer follows symbolic
links to targets outside the working directory.
version 1.35 - Sergey Poznyakoff, 2023-07-18
@ -87,7 +14,7 @@ version 1.35 - Sergey Poznyakoff, 2023-07-18
** Fix interaction of --update with --wildcards.
** When extracting archives into an empty directory, do not create
hard links to files outside that directory.
hard links to files outside that directory.
** Handle partial reads from regular files.
@ -1859,7 +1786,7 @@ Versions 1.07 back to 1.00 by Jay Fenlason.
Copyright 1994-2026 Free Software Foundation, Inc.
Copyright 1994-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

2
README
View File

@ -221,7 +221,7 @@ and share your findings by writing to <bug-tar@gnu.org>.
* Copying
Copyright 1990-2026 Free Software Foundation, Inc.
Copyright 1990-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

View File

@ -5,7 +5,7 @@ Please send comments and problem reports to <bug-tar@gnu.org>.
Copyright 2001-2026 Free Software Foundation, Inc.
Copyright 2001-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

View File

@ -61,16 +61,10 @@ To only fetch auxiliary files from the network, run './bootstrap --pull'.
To only generate files such as 'configure', without accessing the
network, run './bootstrap --gen'.
* Testing
GNU Tar uses Autoconf's Autotest framework for testing; see the tests/
subdirectory, where the file testsuite.at contains the top level.
Run './testsuite --help' to see how to run individual tests.
* Copyright information
Copyright 2007-2026 Free Software Foundation, Inc.
Copyright 2007-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

7
THANKS
View File

@ -283,7 +283,7 @@ Joutsiniemi Tommi Il tj75064@cs.tut.fi
Joy Kendall jak8@world.std.com
Judy Ricker jricker@gdstech.grumman.com
Juha Sarlin juha@tds.kth.se
Jürgen Botz jbotz@orixa.mtholyoke.edu
Jurgen Botz jbotz@orixa.mtholyoke.edu
Jyh-Shyang Wang erik@vsp.ee.nctu.edu.tw
Jörg Schilling schilling@fokus.fraunhofer.de
Jörg Weule weule@cs.uni-duesseldorf.de
@ -308,7 +308,6 @@ Kevin D Quitt drs@netcom.com
Kevin Dalley kevin@aimnet.com
Kimball Collins kpc@ptolemy.arc.nasa.gov
Kimmy Posey kimmyd@bnr.ca
Kirill Furman kfurman@astralinux.ru
Koji Kishi kis@rqa.sony.co.jp
Konno Hiroharu konno@pac.co.jp
Kurt Jaeger pi@lf.net
@ -328,7 +327,6 @@ Mads Martin Joergensen mmj@suse.de
Manfred Weichel Manfred.Weichel@mch.sni.de
Manuel Munier Manuel.Munier@loria.fr
Marc Boucher marc@cam.org
Marc Espie marc.espie.openbsd@gmail.com
Marc Ewing marc@redhat.com
Marcin Matuszewski marcin@frodo.nask.org.pl
Marcus Daniels marcus@sysc.pdx.edu
@ -397,7 +395,7 @@ Pascal Meheut pascal@cnam.cnam.fr
Patrick Fulconis fulco@sig.uvsq.fr
Patrick Timmons timmons@electech.polymtl.ca
Pavel Raiskup praiskup@redhat.com
Paul Eggert eggert@cs.ucla.edu
Paul Eggert eggert@twinsun.com
Paul Kanz paul@icx.com
Paul Mitchell P.Mitchell@surrey.ac.uk
Paul Nevai pali+@osu.edu
@ -537,7 +535,6 @@ Warner Losh imp@boulder.parcplace.com
Warren Dodge warrend@sptekwv3.wv.tek.com
Wayne Christopher wayne@icemcfd.com
Werner Almesberger werner.almesberger@lrc.di.epfl.ch
Wicher Minnaard wicher@nontrivialpursuit.org
William Bader william@nscs.fast.net
William J. Eaton wje@hoffman.rstnu.bcm.tmc.edu
William Kucharski kucharsk@netcom.com

6
TODO
View File

@ -15,7 +15,7 @@ Suggestions for improving GNU tar.
* Add support for a 'pax' command that conforms to POSIX 1003.1-2001.
This would unify paxutils with tar.
* Interoperate better with Jörg Schilling's star implementation.
* Interoperate better with Joerg Schilling's star implementation.
* Add an option to remove files that compare successfully.
@ -25,7 +25,7 @@ Suggestions for improving GNU tar.
It would be useful to be able to use '--remove-files' with '--diff',
to remove all files that compare successfully, when verifying a backup.
* Add tests for the new functionality.
* Add tests for the new functonality.
* Consider this:
@ -45,7 +45,7 @@ Suggestions for improving GNU tar.
* Copyright notice
Copyright 2003-2026 Free Software Foundation, Inc.
Copyright 2003-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

View File

@ -1,6 +1,6 @@
dnl Special Autoconf macros for GNU tar -*- autoconf -*-
dnl Copyright 2009-2026 Free Software Foundation, Inc.
dnl Copyright 2009-2023 Free Software Foundation, Inc.
dnl
dnl This file is part of GNU tar.
dnl

437
bootstrap
View File

@ -3,9 +3,9 @@
# Bootstrap this package from checked-out sources.
scriptversion=2025-06-10.02; # UTC
scriptversion=2022-12-27.07; # UTC
# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# Copyright (C) 2003-2023 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
@ -37,9 +37,9 @@ medir=`dirname "$me"`
# A library of shell functions for autopull.sh, autogen.sh, and bootstrap.
scriptlibversion=2025-12-04.19; # UTC
scriptlibversion=2023-06-06.21; # UTC
# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# Copyright (C) 2003-2023 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
@ -72,7 +72,7 @@ export LC_ALL
# Honor $PERL, but work even if there is none.
PERL="${PERL-perl}"
default_gnulib_url=https://https.git.savannah.gnu.org/git/gnulib.git
default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git
# Copyright year, for the --version output.
copyright_year=`echo "$scriptlibversion" | sed -e 's/[^0-9].*//'`
@ -152,8 +152,7 @@ po_download_command_format=\
"wget --mirror --level=1 -nd -nv -A.po -P '%s' \
https://translationproject.org/latest/%s/"
# When extracting the package name from an AC_INIT invocation,
# prefer a non-empty tarname (4th argument of AC_INIT if given), else
# Prefer a non-empty tarname (4th argument of AC_INIT if given), else
# fall back to the package name (1st argument with munging).
extract_package_name='
/^AC_INIT(\[*/{
@ -165,20 +164,17 @@ extract_package_name='
q
}
s/[],)].*//
s/^GNU //
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
p
}
'
normalize_package_name='
s/^GNU //
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
'
package=$(${AUTOCONF:-autoconf} --trace 'AC_INIT:$4' configure.ac 2>/dev/null)
package=$(${AUTOCONF:-autoconf} --trace AC_INIT:\$4 configure.ac 2>/dev/null)
if test -z "$package"; then
package=$(sed -n "$extract_package_name" configure.ac) \
|| die 'cannot find package name in configure.ac'
fi
package=$(echo "$package" | sed "$normalize_package_name")
gnulib_name=lib$package
build_aux=build-aux
@ -482,9 +478,10 @@ find_tool ()
# --------------------- Preparing GNULIB_SRCDIR for use. ---------------------
# This is part of autopull.sh, but bootstrap needs it too, for self-upgrading.
# cleanup_gnulib fails, removing the directory $gnulib_path first.
cleanup_gnulib() {
status=$?
# XXX It's a bad idea to erase the submodule directory if it contains local
# modifications.
rm -fr "$gnulib_path"
exit $status
}
@ -500,157 +497,92 @@ prepare_GNULIB_SRCDIR ()
# We already checked that $GNULIB_SRCDIR references a directory.
# Verify that it contains a gnulib checkout.
test -f "$GNULIB_SRCDIR/gnulib-tool" \
|| die "Error: --gnulib-srcdir or \$GNULIB_SRCDIR is specified," \
"but does not contain gnulib-tool"
if test -n "$GNULIB_REVISION" && $use_git; then
# The 'git checkout "$GNULIB_REVISION"' command succeeds if the
# GNULIB_REVISION is a commit hash that exists locally, or if it is
# branch name that can be fetched from origin. It fails, however,
# if the GNULIB_REVISION is a commit hash that only exists in
# origin. In this case, we need a 'git fetch' and then retry
# 'git checkout "$GNULIB_REVISION"'.
git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION" 2>/dev/null \
|| { git -C "$GNULIB_SRCDIR" fetch origin \
&& git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION"; } \
|| exit $?
fi
else
if ! $use_git; then
die "Error: --no-git is specified," \
"but neither --gnulib-srcdir nor \$GNULIB_SRCDIR is specified"
fi
if git submodule -h | grep -- --reference > /dev/null; then
:
else
die "git version is too old, git >= 1.6.4 is required"
fi
|| die "Error: --gnulib-srcdir or \$GNULIB_SRCDIR is specified, but does not contain gnulib-tool"
elif $use_git; then
gnulib_path=$(git_modules_config submodule.gnulib.path)
if test -n "$gnulib_path"; then
# A submodule 'gnulib' is configured.
# Get gnulib files. Populate $gnulib_path, updating the submodule.
if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then
# Use GNULIB_REFDIR as a reference.
echo "$0: getting gnulib files..."
git submodule update --init --reference "$GNULIB_REFDIR" "$gnulib_path"\
|| exit $?
test -z "$gnulib_path" && gnulib_path=gnulib
# Get gnulib files. Populate $gnulib_path, possibly updating a
# submodule, for use in the rest of the script.
if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git \
&& git_modules_config submodule.gnulib.url >/dev/null; then
# Use GNULIB_REFDIR as a reference.
echo "$0: getting gnulib files..."
if git submodule -h|grep -- --reference > /dev/null; then
# Prefer the one-liner available in git 1.6.4 or newer.
git submodule update --init --reference "$GNULIB_REFDIR" \
"$gnulib_path" || exit $?
else
# GNULIB_REFDIR is not set or not usable. Ignore it.
if git_modules_config submodule.gnulib.url >/dev/null; then
echo "$0: getting gnulib files..."
git submodule init -- "$gnulib_path" || exit $?
# This fallback allows at least git 1.5.5.
if test -f "$gnulib_path"/gnulib-tool; then
# Since file already exists, assume submodule init already complete.
git submodule update -- "$gnulib_path" || exit $?
else
die "Error: submodule 'gnulib' has no configured url"
# Older git can't clone into an empty directory.
rmdir "$gnulib_path" 2>/dev/null
git clone --reference "$GNULIB_REFDIR" \
"$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \
&& git submodule init -- "$gnulib_path" \
&& git submodule update -- "$gnulib_path" \
|| exit $?
fi
fi
else
gnulib_path='gnulib'
if test ! -d "$gnulib_path"; then
# The subdirectory 'gnulib' does not yet exist. Clone into it.
# GNULIB_REFDIR is not set or not usable. Ignore it.
if git_modules_config submodule.gnulib.url >/dev/null; then
echo "$0: getting gnulib files..."
git submodule init -- "$gnulib_path" || exit $?
git submodule update -- "$gnulib_path" || exit $?
elif [ ! -d "$gnulib_path" ]; then
echo "$0: getting gnulib files..."
trap cleanup_gnulib HUP INT PIPE TERM
gnulib_url=${GNULIB_URL:-$default_gnulib_url}
if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then
# Use GNULIB_REFDIR as a reference.
git clone "$GNULIB_REFDIR" "$gnulib_path" \
&& git -C "$gnulib_path" remote set-url origin "$gnulib_url" \
&& if test -z "$GNULIB_REVISION"; then
git -C "$gnulib_path" pull origin \
&& {
# We want the default branch of "$gnulib_url" (since that's
# the behaviour if GNULIB_REFDIR is not specified), not the
# current branch of "$GNULIB_REFDIR".
default_branch=`LC_ALL=C git -C "$gnulib_path" \
remote show origin \
| sed -n -e 's/^ *HEAD branch: //p'`
test -n "$default_branch" || default_branch='master'
git -C "$gnulib_path" checkout "$default_branch"
}
else
# The 'git checkout "$GNULIB_REVISION"' command succeeds if the
# GNULIB_REVISION is a commit hash that exists locally, or if it
# is a branch name that can be fetched from origin. It fails,
# however, if the GNULIB_REVISION is a commit hash that only
# exists in origin. In this case, we need a 'git fetch' and then
# retry 'git checkout "$GNULIB_REVISION"'.
git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \
|| { git -C "$gnulib_path" fetch origin \
&& git -C "$gnulib_path" checkout "$GNULIB_REVISION"; }
fi \
|| cleanup_gnulib
shallow=
if test -z "$GNULIB_REVISION"; then
if git clone -h 2>&1 | grep -- --depth > /dev/null; then
shallow='--depth 2'
fi
git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \
|| cleanup_gnulib
else
# GNULIB_REFDIR is not set or not usable. Ignore it.
shallow='--depth 2'
if test -z "$GNULIB_REVISION"; then
git clone $shallow "$gnulib_url" "$gnulib_path" \
|| cleanup_gnulib
else
# Only want a shallow checkout of $GNULIB_REVISION, but git does not
# support cloning by commit hash. So attempt a shallow fetch by
# commit hash to minimize the amount of data downloaded and changes
# needed to be processed, which can drastically reduce download and
# processing time for checkout. If the fetch by commit fails, a
# shallow fetch cannot be performed because we do not know what the
# depth of the commit is without fetching all commits. So fall back
# to fetching all commits.
# $GNULIB_REVISION can be a commit id, a tag name, or a branch name.
mkdir -p "$gnulib_path"
# Use a -c option to silence an annoying message
# "hint: Using 'master' as the name for the initial branch."
# (cf. <https://stackoverflow.com/questions/65524512/>).
git -C "$gnulib_path" -c init.defaultBranch=master init
git -C "$gnulib_path" remote add origin "$gnulib_url"
if git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION"
then
# "git fetch" of the specific commit succeeded.
git -C "$gnulib_path" reset --hard FETCH_HEAD \
|| cleanup_gnulib
# "git fetch" does not fetch tags (at least in git version 2.43).
# If $GNULIB_REVISION is a tag (not a commit id or branch name),
# add the tag explicitly.
revision=`git -C "$gnulib_path" log -1 --pretty=format:%H`
branch=`LC_ALL=C git -C "$gnulib_path" remote show origin \
| sed -n -e 's/^ \([^ ]*\) * tracked$/\1/p'`
test "$revision" = "$GNULIB_REVISION" \
|| test "$branch" = "$GNULIB_REVISION" \
|| git -C "$gnulib_path" tag "$GNULIB_REVISION"
else
# Fetch the entire repository.
git -C "$gnulib_path" fetch origin \
|| cleanup_gnulib
git -C "$gnulib_path" checkout "$GNULIB_REVISION" \
|| cleanup_gnulib
fi
if git fetch -h 2>&1 | grep -- --depth > /dev/null; then
shallow='--depth 2'
fi
mkdir -p "$gnulib_path"
# Only want a shallow checkout of $GNULIB_REVISION, but git does not
# support cloning by commit hash. So attempt a shallow fetch by commit
# hash to minimize the amount of data downloaded and changes needed to
# be processed, which can drastically reduce download and processing
# time for checkout. If the fetch by commit fails, a shallow fetch can
# not be performed because we do not know what the depth of the commit
# is without fetching all commits. So fallback to fetching all commits.
git -C "$gnulib_path" init
git -C "$gnulib_path" remote add origin \
${GNULIB_URL:-$default_gnulib_url}
git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \
|| git -C "$gnulib_path" fetch origin \
|| cleanup_gnulib
git -C "$gnulib_path" reset --hard FETCH_HEAD
fi
trap - HUP INT PIPE TERM
else
# The subdirectory 'gnulib' already exists.
if test -n "$GNULIB_REVISION"; then
if test -d "$gnulib_path/.git"; then
# The 'git checkout "$GNULIB_REVISION"' command succeeds if the
# GNULIB_REVISION is a commit hash that exists locally, or if it
# is a branch name that can be fetched from origin. It fails,
# however, if the GNULIB_REVISION is a commit hash that only
# exists in origin. In this case, we need a 'git fetch' and then
# retry 'git checkout "$GNULIB_REVISION"'.
git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \
|| { git -C "$gnulib_path" fetch origin \
&& git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } \
|| exit $?
else
die "Error: GNULIB_REVISION is specified in bootstrap.conf," \
"but '$gnulib_path' contains no git history"
fi
fi
fi
fi
# Verify that $gnulib_path contains a gnulib checkout.
test -f "$gnulib_path/gnulib-tool" \
|| die "Error: '$gnulib_path' is supposed to contain a gnulib checkout," \
"but does not contain gnulib-tool"
GNULIB_SRCDIR=$gnulib_path
# Verify that the submodule contains a gnulib checkout.
test -f "$gnulib_path/gnulib-tool" \
|| die "Error: $gnulib_path is supposed to contain a gnulib checkout, but does not contain gnulib-tool"
fi
# XXX Should this be done if $use_git is false?
if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \
&& ! git_modules_config submodule.gnulib.url >/dev/null; then
(cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib
fi
# $GNULIB_SRCDIR now points to the version of gnulib to use, and
# we no longer need to use git or $gnulib_path below here.
}
@ -662,8 +594,7 @@ upgrade_bootstrap ()
if test -f "$medir"/bootstrap-funclib.sh; then
update_lib=true
{ cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/top/bootstrap" \
&& cmp -s "$medir"/bootstrap-funclib.sh \
"$GNULIB_SRCDIR/top/bootstrap-funclib.sh" \
&& cmp -s "$medir"/bootstrap-funclib.sh "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" \
&& cmp -s "$medir"/autopull.sh "$GNULIB_SRCDIR/top/autopull.sh" \
&& cmp -s "$medir"/autogen.sh "$GNULIB_SRCDIR/top/autogen.sh"; \
}
@ -680,18 +611,10 @@ upgrade_bootstrap ()
a) ignored=--;;
*) ignored=ignored;;
esac
u=$update_lib
exec sh -c \
'{ if '$u' && test -f "$1"; then cp "$1" "$3"; else cp "$2" "$3"; fi; } &&
{ if '$u' && test -f "$4"; then cp "$4" "$5"; else rm -f "$5"; fi; } &&
{ if '$u' && test -f "$6"; then cp "$6" "$7"; else rm -f "$7"; fi; } &&
{ if '$u' && test -f "$8"; then cp "$8" "$9"; else rm -f "$9"; fi; } &&
shift && shift && shift && shift && shift &&
shift && shift && shift && shift &&
exec "${CONFIG_SHELL-/bin/sh}" "$@"' \
'{ if '$update_lib' && test -f "$1"; then cp "$1" "$3"; else cp "$2" "$3"; fi; } && { if '$update_lib' && test -f "$4"; then cp "$4" "$5"; else rm -f "$5"; fi; } && { if '$update_lib' && test -f "$6"; then cp "$6" "$7"; else rm -f "$7"; fi; } && { if '$update_lib' && test -f "$8"; then cp "$8" "$9"; else rm -f "$9"; fi; } && shift && shift && shift && shift && shift && shift && shift && shift && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \
$ignored \
"$GNULIB_SRCDIR/top/bootstrap" "$GNULIB_SRCDIR/build-aux/bootstrap" \
"$medir/bootstrap" \
"$GNULIB_SRCDIR/top/bootstrap" "$GNULIB_SRCDIR/build-aux/bootstrap" "$medir/bootstrap" \
"$GNULIB_SRCDIR/top/bootstrap-funclib.sh" "$medir/bootstrap-funclib.sh" \
"$GNULIB_SRCDIR/top/autopull.sh" "$medir/autopull.sh" \
"$GNULIB_SRCDIR/top/autogen.sh" "$medir/autogen.sh" \
@ -712,8 +635,7 @@ fi
autopull_usage() {
cat <<EOF
Usage: $me [OPTION]...
Bootstrap this package from the checked-out sources, phase 1:
Pull files from the network.
Bootstrap this package from the checked-out sources.
Optional environment variables:
GNULIB_SRCDIR Specifies the local directory where gnulib
@ -725,24 +647,21 @@ Optional environment variables:
Use this if you already have gnulib sources
and history on your machine, and do not want
to waste your bandwidth downloading them again.
GNULIB_URL URL of the gnulib repository. The default is
$default_gnulib_url,
which is Gnulib's upstream repository.
GNULIB_URL Cloneable URL of the gnulib repository.
Options:
--bootstrap-sync If this bootstrap script is not identical to
--bootstrap-sync if this bootstrap script is not identical to
the version in the local gnulib sources,
update this script, and then restart it with
/bin/sh or the shell \$CONFIG_SHELL.
--no-bootstrap-sync Do not check whether bootstrap is out of sync.
--force Attempt to bootstrap even if the sources seem
not to have been checked out.
--no-git Do not use git to update gnulib. Requires that
\$GNULIB_SRCDIR points to a gnulib repository
with the correct revision.
--skip-po Do not download *.po files.
/bin/sh or the shell \$CONFIG_SHELL
--no-bootstrap-sync do not check whether bootstrap is out of sync
--force attempt to bootstrap even if the sources seem
not to have been checked out
--no-git do not use git to update gnulib. Requires that
\$GNULIB_SRCDIR or the --gnulib-srcdir option
points to a gnulib repository with the correct
revision
--skip-po do not download po files
EOF
bootstrap_print_option_usage_hook
cat <<EOF
@ -754,21 +673,21 @@ are honored.
Gnulib sources can be fetched in various ways:
* If the environment variable GNULIB_SRCDIR is set, then sources are
fetched from that local directory. If it is a git repository and the
configuration variable GNULIB_REVISION is set in bootstrap.conf, then
that revision is checked out.
* If the environment variable GNULIB_SRCDIR is set (either as an
environment variable or via the --gnulib-srcdir option), then sources
are fetched from that local directory. If it is a git repository and
the configuration variable GNULIB_REVISION is set in bootstrap.conf,
then that revision is checked out.
* Otherwise, if this package is in a git repository with a 'gnulib'
submodule configured, then that submodule is initialized and updated
and sources are fetched from there. If the environment variable
GNULIB_REFDIR is set and is a git repository, then it is used as a
reference.
and sources are fetched from there. If GNULIB_REFDIR is set (either
as an environment variable or via the --gnulib-refdir option) and is
a git repository, then it is used as a reference.
* Otherwise, if the 'gnulib' directory does not exist, Gnulib sources
are cloned into that directory using git from \$GNULIB_URL, defaulting
to $default_gnulib_url; if GNULIB_REFDIR is set and is a git repository
its contents may be used to accelerate the process.
to $default_gnulib_url.
If the configuration variable GNULIB_REVISION is set in bootstrap.conf,
then that revision is checked out.
@ -828,12 +747,9 @@ autopull()
done
$use_git || test -n "$GNULIB_SRCDIR" \
|| die "Error: --no-git requires \$GNULIB_SRCDIR environment variable" \
"or --gnulib-srcdir option"
|| die "Error: --no-git requires \$GNULIB_SRCDIR environment variable or --gnulib-srcdir option"
test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \
|| die "Error: \$GNULIB_SRCDIR environment variable" \
"or --gnulib-srcdir option is specified," \
"but does not denote a directory"
|| die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir option is specified, but does not denote a directory"
if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
die "Running this script from a non-checked-out distribution is risky."
@ -844,7 +760,7 @@ autopull()
if $use_gnulib || $bootstrap_sync; then
prepare_GNULIB_SRCDIR
if $bootstrap_sync; then
upgrade_bootstrap "$@"
upgrade_bootstrap
fi
fi
@ -861,8 +777,7 @@ autopull()
elif check_exists git-merge-changelog; then
echo "$0: initializing git-merge-changelog driver"
git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
git config merge.merge-changelog.driver \
'git-merge-changelog %O %A %B "%Y"'
git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
else
echo "$0: consider installing git-merge-changelog from gnulib"
fi
@ -896,10 +811,7 @@ autopull()
uninitialized=`echo "$uninitialized" | grep -v '^gnulib$'`
fi
if test -n "$uninitialized"; then
uninit_comma=`echo "$uninitialized" | tr '\n' ',' | sed -e 's|,$|.|'`
die "Some git submodules are not initialized: "$uninit_comma \
"Either use option '--no-git'," \
"or run 'git submodule update --init' and bootstrap again."
die "Some git submodules are not initialized: "`echo "$uninitialized" | tr '\n' ',' | sed -e 's|,$|.|'`" Either use option '--no-git', or run 'git submodule update --init' and bootstrap again."
fi
fi
@ -937,7 +849,9 @@ update_po_files() {
&& ls "$ref_po_dir"/*.po 2>/dev/null |
sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do
langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g')
test "$langs" = '*' && langs=x
for po in $langs; do
case $po in x) continue;; esac
new_po="$ref_po_dir/$po.po"
cksum_file="$ref_po_dir/$po.s1"
@ -956,8 +870,7 @@ update_po_files() {
autogen_usage() {
cat <<EOF
Usage: $me [OPTION]...
Bootstrap this package from the checked-out sources, phase 2:
Generate files from local files (no network access).
Bootstrap this package from the checked-out sources.
Optional environment variables:
GNULIB_SRCDIR Specifies the local directory where gnulib
@ -966,9 +879,9 @@ Optional environment variables:
you want to use these sources.
Options:
--copy Copy files instead of creating symbolic links.
--force Attempt to bootstrap even if the sources seem
not to have been checked out.
--copy copy files instead of creating symbolic links
--force attempt to bootstrap even if the sources seem
not to have been checked out
EOF
bootstrap_print_option_usage_hook
cat <<EOF
@ -1162,8 +1075,7 @@ autogen()
done
test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \
|| die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir" \
"option is specified, but does not denote a directory"
|| die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir option is specified, but does not denote a directory"
if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
die "Running this script from a non-checked-out distribution is risky."
@ -1171,8 +1083,7 @@ autogen()
if $use_gnulib; then
if test -z "$GNULIB_SRCDIR"; then
gnulib_path=$(test -f .gitmodules &&
git config --file .gitmodules submodule.gnulib.path)
gnulib_path=$(test -f .gitmodules && git config --file .gitmodules submodule.gnulib.path)
test -z "$gnulib_path" && gnulib_path=gnulib
GNULIB_SRCDIR=$gnulib_path
fi
@ -1301,20 +1212,6 @@ autogen()
$gnulib_tool $gnulib_tool_options --import $gnulib_modules \
|| die "gnulib-tool failed"
if test $with_gettext = yes && test ! -f $m4_base/gettext.m4; then
# The gnulib-tool invocation has removed $m4_base/gettext.m4, that the
# AUTOPOINT invocation had installed. This can occur when the gnulib
# module 'gettext' was previously present but is now not present any more.
# Repeat the AUTOPOINT invocation and the gnulib-tool invocation.
echo "$0: $AUTOPOINT --force"
$AUTOPOINT --force || return
echo "$0: $gnulib_tool $gnulib_tool_options --import ..."
$gnulib_tool $gnulib_tool_options --import $gnulib_modules \
|| die "gnulib-tool failed"
fi
for file in $gnulib_files; do
symlink_to_dir "$GNULIB_SRCDIR" $file \
|| die "failed to symlink $file"
@ -1338,7 +1235,7 @@ autogen()
# Invoke autoreconf with --force --install to ensure upgrades of tools
# such as ylwrap.
AUTORECONFFLAGS="--verbose --install --force $ACLOCAL_FLAGS"
AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS"
AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive"
# Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
@ -1400,7 +1297,7 @@ autogen()
|| die 'cannot generate runtime-po/Makevars'
# Copy identical files from po to runtime-po.
cp -p po/Makefile.in.in po/*-quot po/*.header po/*.sed po/*.sin runtime-po
(cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
fi
fi
@ -1412,9 +1309,9 @@ autogen()
# ----------------------------------------------------------------------------
# Local Variables:
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptlibversion="
# time-stamp-format: "%Y-%02m-%02d.%02H"
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
@ -1428,55 +1325,42 @@ Optional environment variables:
GNULIB_SRCDIR Specifies the local directory where gnulib
sources reside. Use this if you already
have gnulib sources on your machine, and
you want to use these sources.
GNULIB_REFDIR Specifies the local directory where a gnulib
repository (with a .git subdirectory) resides.
Use this if you already have gnulib sources
and history on your machine, and do not want
to waste your bandwidth downloading them again.
Only used for phase 1 (--pull).
GNULIB_URL URL of the gnulib repository. The default is
$default_gnulib_url,
which is Gnulib's upstream repository.
Only used for phase 1 (--pull).
do not want to waste your bandwidth downloading
them again.
GNULIB_URL Cloneable URL of the gnulib repository.
Options:
--pull Do phase 1: Pull files from the network.
--gen Do phase 2: Generate files from local files
(no network access).
--pull Do phase 1: pull files from network
--gen Do phase 2: generate from local files.
(The default is to do both phases.)
--gnulib-srcdir=DIRNAME Specifies the local directory where gnulib
--gnulib-srcdir=DIRNAME specify the local directory where gnulib
sources reside. Use this if you already
have gnulib sources on your machine, and
you want to use these sources. Defaults
to \$GNULIB_SRCDIR.
--gnulib-refdir=DIRNAME Specifies the local directory where a gnulib
to \$GNULIB_SRCDIR
--gnulib-refdir=DIRNAME specify the local directory where a gnulib
repository (with a .git subdirectory) resides.
Use this if you already have gnulib sources
and history on your machine, and do not want
to waste your bandwidth downloading them again.
Defaults to \$GNULIB_REFDIR.
Only used for phase 1 (--pull).
Defaults to \$GNULIB_REFDIR
--bootstrap-sync If this bootstrap script is not identical to
--bootstrap-sync if this bootstrap script is not identical to
the version in the local gnulib sources,
update this script, and then restart it with
/bin/sh or the shell \$CONFIG_SHELL.
--no-bootstrap-sync Do not check whether bootstrap is out of sync.
/bin/sh or the shell \$CONFIG_SHELL
--no-bootstrap-sync do not check whether bootstrap is out of sync
--copy Copy files instead of creating symbolic links.
Only used for phase 2 (--gen).
--force Attempt to bootstrap even if the sources seem
not to have been checked out.
--no-git Do not use git to update gnulib. Requires that
--copy copy files instead of creating symbolic links
--force attempt to bootstrap even if the sources seem
not to have been checked out
--no-git do not use git to update gnulib. Requires that
\$GNULIB_SRCDIR or the --gnulib-srcdir option
points to a gnulib repository with the correct
revision.
Only used for phase 1 (--pull).
--skip-po Do not download *.po files.
Only used for phase 1 (--pull).
revision
--skip-po do not download po files
EOF
bootstrap_print_option_usage_hook
cat <<EOF
@ -1488,11 +1372,11 @@ are honored.
Gnulib sources can be fetched in various ways:
* If GNULIB_SRCDIR is set (either as an environment variable or via the
--gnulib-srcdir option), then sources are fetched from that local
directory. If it is a git repository and the configuration variable
GNULIB_REVISION is set in bootstrap.conf, then that revision is
checked out.
* If the environment variable GNULIB_SRCDIR is set (either as an
environment variable or via the --gnulib-srcdir option), then sources
are fetched from that local directory. If it is a git repository and
the configuration variable GNULIB_REVISION is set in bootstrap.conf,
then that revision is checked out.
* Otherwise, if this package is in a git repository with a 'gnulib'
submodule configured, then that submodule is initialized and updated
@ -1502,8 +1386,7 @@ Gnulib sources can be fetched in various ways:
* Otherwise, if the 'gnulib' directory does not exist, Gnulib sources
are cloned into that directory using git from \$GNULIB_URL, defaulting
to $default_gnulib_url; if GNULIB_REFDIR is set and is a git repository
its contents may be used to accelerate the process.
to $default_gnulib_url.
If the configuration variable GNULIB_REVISION is set in bootstrap.conf,
then that revision is checked out.
@ -1575,11 +1458,9 @@ done
$pull || $gen || pull=true gen=true
$use_git || test -n "$GNULIB_SRCDIR" \
|| die "Error: --no-git requires \$GNULIB_SRCDIR environment variable" \
"or --gnulib-srcdir option"
|| die "Error: --no-git requires \$GNULIB_SRCDIR environment variable or --gnulib-srcdir option"
test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \
|| die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir" \
"option is specified, but does not denote a directory"
|| die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir option is specified, but does not denote a directory"
if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then
die "Bootstrapping from a non-checked-out distribution is risky."
@ -1589,7 +1470,7 @@ check_build_prerequisites $use_git
if $bootstrap_sync; then
prepare_GNULIB_SRCDIR
upgrade_bootstrap "$@"
upgrade_bootstrap
# Since we have now upgraded if needed, no need to try it a second time below.
bootstrap_sync=false
fi
@ -1602,11 +1483,7 @@ export GNULIB_REFDIR
if $pull && { $use_git || test -z "$SKIP_PO"; }; then
autopull \
`if $bootstrap_sync; then
echo ' --bootstrap-sync'
else
echo ' --no-bootstrap-sync'
fi` \
`if $bootstrap_sync; then echo ' --bootstrap-sync'; else echo ' --no-bootstrap-sync'; fi` \
`if test -z "$checkout_only_file"; then echo ' --force'; fi` \
`if ! $use_git; then echo ' --no-git'; fi` \
`if test -n "$SKIP_PO"; then echo ' --skip-po'; fi` \
@ -1614,18 +1491,18 @@ if $pull && { $use_git || test -z "$SKIP_PO"; }; then
fi
if $gen; then
autogen \
`if $copy; then echo ' --copy'; fi` \
`if test -z "$checkout_only_file"; then echo ' --force'; fi` \
|| die "could not generate auxiliary files"
autogen \
`if $copy; then echo ' --copy'; fi` \
`if test -z "$checkout_only_file"; then echo ' --force'; fi` \
|| die "could not generate auxiliary files"
fi
# ----------------------------------------------------------------------------
# Local Variables:
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%Y-%02m-%02d.%02H"
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@ -1,6 +1,6 @@
# Bootstrap configuration for GNU tar.
# Copyright 2006-2026 Free Software Foundation, Inc.
# Copyright 2006-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -20,12 +20,10 @@
source_base=gnu
gnulib_name=libgnu
# We don't need these modules, even though gnulib-tool ordinarily
# includes them because of dependencies on the modules 'exclude' and 'regex'.
# We don't need these modules, even though gnulib-tool mistakenly
# includes them because of gettext dependencies.
avoided_gnulib_modules='
--avoid=lock
--avoid=mbuiter
--avoid=mbuiterf
'

View File

@ -1,6 +1,6 @@
# Configure template for GNU tar. -*- autoconf -*-
# Copyright 1991, 1994-2010, 2013-2026 Free Software Foundation, Inc.
# Copyright 1991, 1994-2010, 2013-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_INIT([GNU tar], [1.35.90], [bug-tar@gnu.org])
AC_INIT([GNU tar], [1.35], [bug-tar@gnu.org])
AC_CONFIG_SRCDIR([src/tar.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
@ -34,7 +34,19 @@ AC_PROG_YACC
gl_EARLY
AC_CHECK_TOOLS([AR], [ar])
AC_CHECK_HEADERS_ONCE([linux/fd.h sys/mtio.h])
AC_SYS_LARGEFILE
AC_CHECK_HEADERS_ONCE(fcntl.h linux/fd.h memory.h net/errno.h \
sgtty.h string.h \
sys/param.h sys/device.h sys/gentape.h \
sys/inet.h sys/io/trioctl.h \
sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \
unistd.h locale.h)
AC_CHECK_HEADERS([sys/buf.h], [], [],
[#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif])
AC_HEADER_MAJOR
@ -82,16 +94,12 @@ AC_TYPE_MODE_T
AC_TYPE_PID_T
AC_TYPE_OFF_T
AC_TYPE_UID_T
# Taken from GNU/Linux, and should be good enough on platforms
# lacking these types.
AC_CHECK_TYPE([dev_t], [unsigned long long int])
AC_CHECK_TYPE([ino_t], [unsigned long long int])
# Taken from GNU/Linux, and should be good enough on platforms
# lacking these types.
AC_CHECK_TYPE([major_t], [unsigned int])
AC_CHECK_TYPE([minor_t], [unsigned int])
AC_CHECK_TYPE(major_t, , AC_DEFINE(major_t, int,
[Type of major device numbers.]))
AC_CHECK_TYPE(minor_t, , AC_DEFINE(minor_t, int,
[Type of minor device numbers.]))
AC_CHECK_TYPE(dev_t, unsigned)
AC_CHECK_TYPE(ino_t, unsigned)
gt_TYPE_SSIZE_T
@ -112,8 +120,9 @@ AC_DEFINE([GNULIB_WCHAR_SINGLE_LOCALE], [1],
like mbrtowc only after setting the locale, and never change the
locale once set.])
if test $COMPILE_ERROR_C = 1; then
# This means that Gnulib's 'error' function will be used. It precedes
if test $ac_cv_lib_error_at_line = no; then
# This means that the error() function is not present in libc, so
# the one from gnulib will be used instead. This function precedes
# error messages it prints with the program name as returned by getprogname()
# call, instead of using the name set by set_program_name.
# Install workaround.
@ -170,6 +179,7 @@ if test "$gl_gcc_warnings" = yes; then
nw="$nw -Winline" # It's OK to not inline.
nw="$nw -Wstrict-overflow" # It's OK to optimize strictly.
nw="$nw -Wsuggest-attribute=pure" # Too many warnings for now.
nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings
nw="$nw -Wstack-protector"
gl_MANYWARN_ALL_GCC([ws])
@ -225,7 +235,13 @@ fi
TAR_HEADERS_ATTR_XATTR_H
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync mkfifo waitpid])
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
AC_CHECK_DECLS([time],,, [#include <time.h>])
AC_REPLACE_FUNCS(waitpid)
AC_ARG_VAR([RSH], [Configure absolute path to default remote shell binary])
AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
@ -249,7 +265,9 @@ AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
fi
done
fi])
if test $tar_cv_path_RSH != "no"; then
if test $tar_cv_path_RSH = no; then
AC_CHECK_HEADERS(netdb.h)
else
AC_DEFINE_UNQUOTED(REMOTE_SHELL, "$tar_cv_path_RSH",
[Define to the full path of your rsh, if any.])
fi
@ -343,7 +361,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_QUOTING_STYLE, $DEFAULT_QUOTING_STYLE,
# Iconv
AM_ICONV
AC_CHECK_HEADERS_ONCE([iconv.h])
AC_CHECK_HEADERS(iconv.h)
AC_CHECK_TYPE(iconv_t,:,
AC_DEFINE(iconv_t, int,
[Conversion descriptor type]),

View File

@ -1,5 +1,5 @@
%%comments:
Copyright 2004-2026 Free Software Foundation, Inc.
Copyright 2004-2023 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@ -59,12 +59,12 @@ programs (using pipes); tar can even access remote devices or files
%%developers:
John Gilmore,
Thomas Bushnell,
Paul Eggert <eggert@cs.ucla.edu>,
Sergey Poznyakoff <gray@gnu.org>
Paul Eggert <eggert@twinsun.com>,
Sergey Poznyakoff <gray@Mirddin.farlep.net>
%%contributors: Jay Fenlason,
Joy Kendall,
François Pinard
Francois Pinard <pinard@iro.umontreal.ca>
%%source-tarball: ftp://ftp.gnu.org/pub/gnu/tar/tar-1.15.1.tar.gz
%%source-info: http://savannah.gnu.org/projects/tar

2
doc/.gitignore vendored
View File

@ -23,7 +23,5 @@ tar.toc
tar.tp
tar.vr
version.texi
/gendocs_template
/gendocs_template_min
/parse-datetime.texi
/rmt.8

View File

@ -1,6 +1,6 @@
# Makefile for GNU tar documentation.
# Copyright 1994-2026 Free Software Foundation, Inc.
# Copyright 1994-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -138,7 +138,7 @@ check-docs:
clean-local:
rm -rf manual
GENDOCS=$(top_srcdir)/build-aux/gendocs.sh
GENDOCS=$(srcdir)/gendocs.sh
TEXI2DVI=texi2dvi -E
@ -152,3 +152,4 @@ manual:
$(GENDOCS) --texi2html tar 'GNU tar manual'
manual-rebuild: clean-local manual

View File

@ -18,7 +18,7 @@ documentation files in various formats. If the doc/manual directory
already exists, it will be removed prior to rebuilding.
The command produces very copious output. We advise you to examine it
closely to make sure no error messages slip your attention.
closely to make sure no error messages slip your attention.
For the completeness sake, there are two more Makefile goals related
to the online manual:
@ -55,7 +55,7 @@ before publishing:
To publish the created manual, change to the tar top-level directory
and run:
rsync -avz --exclude CVS --delete manual ~/websrc/tar
rsync -avz --exclude CVS --delete manual ~/websrc/tar
This will synchronize the newly created manual pages with the content
of the CVS sandbox. Then, change to the ~/websrc/tar directory and
@ -67,7 +67,7 @@ to the repository:
Then commit your changes:
cvs commit
cvs commit
Once the changes are committed to CVS a job is scheduled on the server,
which synchronizes them with the content of the directory served by

View File

@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2006--2026 Free Software Foundation, Inc.
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
@c Written by Sergey Poznyakoff
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.

View File

@ -5,7 +5,8 @@
@c hence no sectioning command or @node.
@display
Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
Copyright @copyright{} 2000--2002, 2007--2008, 2022 Free Software
Foundation, Inc.
@uref{https://fsf.org/}
Everyone is permitted to copy and distribute verbatim copies

512
doc/gendocs.sh Executable file
View File

@ -0,0 +1,512 @@
#!/bin/sh -e
# gendocs.sh -- generate a GNU manual in many formats. This script is
# mentioned in maintain.texi. See the help message below for usage details.
scriptversion=2021-03-01.13
# Copyright 2003-2023 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
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# Original author: Mohit Agarwal.
# Send bug reports and any other correspondence to bug-gnulib@gnu.org.
#
# The latest version of this script, and the companion template, is
# available from the Gnulib repository:
#
# https://git.savannah.gnu.org/cgit/gnulib.git/tree/build-aux/gendocs.sh
# https://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/gendocs_template
# TODO:
# - image importing was only implemented for HTML generated by
# makeinfo. But it should be simple enough to adjust.
# - images are not imported in the source tarball. All the needed
# formats (PDF, PNG, etc.) should be included.
prog=`basename "$0"`
srcdir=`pwd`
scripturl="https://git.savannah.gnu.org/cgit/gnulib.git/plain/build-aux/gendocs.sh"
templateurl="https://git.savannah.gnu.org/cgit/gnulib.git/plain/doc/gendocs_template"
: ${SETLANG="env LANG= LC_MESSAGES= LC_ALL= LANGUAGE="}
: ${MAKEINFO="makeinfo"}
: ${TEXI2DVI="texi2dvi"}
: ${DOCBOOK2HTML="docbook2html"}
: ${DOCBOOK2PDF="docbook2pdf"}
: ${DOCBOOK2TXT="docbook2txt"}
: ${GENDOCS_TEMPLATE_DIR="."}
: ${PERL='perl'}
: ${TEXI2HTML="texi2html"}
unset CDPATH
unset use_texi2html
MANUAL_TITLE=
PACKAGE=
EMAIL=webmasters@gnu.org # please override with --email
commonarg= # passed to all makeinfo/texi2html invcations.
dirargs= # passed to all tools (-I dir).
dirs= # -I directories.
htmlarg="--css-ref=/software/gnulib/manual.css -c TOP_NODE_UP_URL=/manual"
default_htmlarg=true
infoarg=--no-split
generate_ascii=true
generate_html=true
generate_info=true
generate_tex=true
outdir=manual
source_extra=
split=node
srcfile=
texarg="-t @finalout"
version="gendocs.sh $scriptversion
Copyright 2021 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
usage="Usage: $prog [OPTION]... PACKAGE MANUAL-TITLE
Generate output in various formats from PACKAGE.texinfo (or .texi or
.txi) source. See the GNU Maintainers document for a more extensive
discussion:
https://www.gnu.org/prep/maintain_toc.html
Options:
--email ADR use ADR as contact in generated web pages; always give this.
-s SRCFILE read Texinfo from SRCFILE, instead of PACKAGE.{texinfo|texi|txi}
-o OUTDIR write files into OUTDIR, instead of manual/.
-I DIR append DIR to the Texinfo search path.
--common ARG pass ARG in all invocations.
--html ARG pass ARG to makeinfo or texi2html for HTML targets,
instead of '$htmlarg'.
--info ARG pass ARG to makeinfo for Info, instead of --no-split.
--no-ascii skip generating the plain text output.
--no-html skip generating the html output.
--no-info skip generating the info output.
--no-tex skip generating the dvi and pdf output.
--source ARG include ARG in tar archive of sources.
--split HOW make split HTML by node, section, chapter; default node.
--tex ARG pass ARG to texi2dvi for DVI and PDF, instead of -t @finalout.
--texi2html use texi2html to make HTML target, with all split versions.
--docbook convert through DocBook too (xml, txt, html, pdf).
--help display this help and exit successfully.
--version display version information and exit successfully.
Simple example: $prog --email bug-gnu-emacs@gnu.org emacs \"GNU Emacs Manual\"
Typical sequence:
cd PACKAGESOURCE/doc
wget \"$scripturl\"
wget \"$templateurl\"
$prog --email BUGLIST MANUAL \"GNU MANUAL - One-line description\"
Output will be in a new subdirectory \"manual\" (by default;
use -o OUTDIR to override). Move all the new files into your web CVS
tree, as explained in the Web Pages node of maintain.texi.
Please use the --email ADDRESS option so your own bug-reporting
address will be used in the generated HTML pages.
MANUAL-TITLE is included as part of the HTML <title> of the overall
manual/index.html file. It should include the name of the package being
documented. manual/index.html is created by substitution from the file
$GENDOCS_TEMPLATE_DIR/gendocs_template. (Feel free to modify the
generic template for your own purposes.)
If you have several manuals, you'll need to run this script several
times with different MANUAL values, specifying a different output
directory with -o each time. Then write (by hand) an overall index.html
with links to them all.
If a manual's Texinfo sources are spread across several directories,
first copy or symlink all Texinfo sources into a single directory.
(Part of the script's work is to make a tar.gz of the sources.)
As implied above, by default monolithic Info files are generated.
If you want split Info, or other Info options, use --info to override.
You can set the environment variables MAKEINFO, TEXI2DVI, TEXI2HTML,
and PERL to control the programs that get executed, and
GENDOCS_TEMPLATE_DIR to control where the gendocs_template file is
looked for. With --docbook, the environment variables DOCBOOK2HTML,
DOCBOOK2PDF, and DOCBOOK2TXT are also consulted.
By default, makeinfo and texi2dvi are run in the default (English)
locale, since that's the language of most Texinfo manuals. If you
happen to have a non-English manual and non-English web site, see the
SETLANG setting in the source.
Email bug reports or enhancement requests to bug-gnulib@gnu.org.
"
while test $# -gt 0; do
case $1 in
-s) shift; srcfile=$1;;
-o) shift; outdir=$1;;
-I) shift; dirargs="$dirargs -I '$1'"; dirs="$dirs $1";;
--common) shift; commonarg=$1;;
--docbook) docbook=yes;;
--email) shift; EMAIL=$1;;
--html) shift; default_htmlarg=false; htmlarg=$1;;
--info) shift; infoarg=$1;;
--no-ascii) generate_ascii=false;;
--no-html) generate_ascii=false;;
--no-info) generate_info=false;;
--no-tex) generate_tex=false;;
--source) shift; source_extra=$1;;
--split) shift; split=$1;;
--tex) shift; texarg=$1;;
--texi2html) use_texi2html=1;;
--help) echo "$usage"; exit 0;;
--version) echo "$version"; exit 0;;
-*)
echo "$0: Unknown option \`$1'." >&2
echo "$0: Try \`--help' for more information." >&2
exit 1;;
*)
if test -z "$PACKAGE"; then
PACKAGE=$1
elif test -z "$MANUAL_TITLE"; then
MANUAL_TITLE=$1
else
echo "$0: extra non-option argument \`$1'." >&2
exit 1
fi;;
esac
shift
done
# makeinfo uses the dirargs, but texi2dvi doesn't.
commonarg=" $dirargs $commonarg"
# For most of the following, the base name is just $PACKAGE
base=$PACKAGE
if $default_htmlarg && test -n "$use_texi2html"; then
# The legacy texi2html doesn't support TOP_NODE_UP_URL
htmlarg="--css-ref=/software/gnulib/manual.css"
fi
if test -n "$srcfile"; then
# but here, we use the basename of $srcfile
base=`basename "$srcfile"`
case $base in
*.txi|*.texi|*.texinfo) base=`echo "$base"|sed 's/\.[texinfo]*$//'`;;
esac
PACKAGE=$base
elif test -s "$srcdir/$PACKAGE.texinfo"; then
srcfile=$srcdir/$PACKAGE.texinfo
elif test -s "$srcdir/$PACKAGE.texi"; then
srcfile=$srcdir/$PACKAGE.texi
elif test -s "$srcdir/$PACKAGE.txi"; then
srcfile=$srcdir/$PACKAGE.txi
else
echo "$0: cannot find .texinfo or .texi or .txi for $PACKAGE in $srcdir." >&2
exit 1
fi
if test ! -r $GENDOCS_TEMPLATE_DIR/gendocs_template; then
echo "$0: cannot read $GENDOCS_TEMPLATE_DIR/gendocs_template." >&2
echo "$0: it is available from $templateurl." >&2
exit 1
fi
# Function to return size of $1 in something resembling kilobytes.
calcsize()
{
size=`ls -ksl $1 | awk '{print $1}'`
echo $size
}
# copy_images OUTDIR HTML-FILE...
# -------------------------------
# Copy all the images needed by the HTML-FILEs into OUTDIR.
# Look for them in . and the -I directories; this is simpler than what
# makeinfo supports with -I, but hopefully it will suffice.
copy_images()
{
local odir
odir=$1
shift
$PERL -n -e "
BEGIN {
\$me = '$prog';
\$odir = '$odir';
@dirs = qw(. $dirs);
}
" -e '
/<img src="(.*?)"/g && ++$need{$1};
END {
#print "$me: @{[keys %need]}\n"; # for debugging, show images found.
FILE: for my $f (keys %need) {
for my $d (@dirs) {
if (-f "$d/$f") {
use File::Basename;
my $dest = dirname ("$odir/$f");
#
use File::Path;
-d $dest || mkpath ($dest)
|| die "$me: cannot mkdir $dest: $!\n";
#
use File::Copy;
copy ("$d/$f", $dest)
|| die "$me: cannot copy $d/$f to $dest: $!\n";
next FILE;
}
}
die "$me: $ARGV: cannot find image $f\n";
}
}
' -- "$@" || exit 1
}
case $outdir in
/*) abs_outdir=$outdir;;
*) abs_outdir=$srcdir/$outdir;;
esac
echo "Making output for $srcfile"
echo " in `pwd`"
mkdir -p "$outdir/"
#
if $generate_info; then
cmd="$SETLANG $MAKEINFO -o $PACKAGE.info $commonarg $infoarg \"$srcfile\""
echo "Generating info... ($cmd)"
rm -f $PACKAGE.info* # get rid of any strays
eval "$cmd"
tar czf "$outdir/$PACKAGE.info.tar.gz" $PACKAGE.info*
ls -l "$outdir/$PACKAGE.info.tar.gz"
info_tgz_size=`calcsize "$outdir/$PACKAGE.info.tar.gz"`
# do not mv the info files, there's no point in having them available
# separately on the web.
fi # end info
#
if $generate_tex; then
cmd="$SETLANG $TEXI2DVI $dirargs $texarg \"$srcfile\""
printf "\nGenerating dvi... ($cmd)\n"
eval "$cmd"
# compress/finish dvi:
gzip -f -9 $PACKAGE.dvi
dvi_gz_size=`calcsize $PACKAGE.dvi.gz`
mv $PACKAGE.dvi.gz "$outdir/"
ls -l "$outdir/$PACKAGE.dvi.gz"
cmd="$SETLANG $TEXI2DVI --pdf $dirargs $texarg \"$srcfile\""
printf "\nGenerating pdf... ($cmd)\n"
eval "$cmd"
pdf_size=`calcsize $PACKAGE.pdf`
mv $PACKAGE.pdf "$outdir/"
ls -l "$outdir/$PACKAGE.pdf"
fi # end tex (dvi + pdf)
#
if $generate_ascii; then
opt="-o $PACKAGE.txt --no-split --no-headers $commonarg"
cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
printf "\nGenerating ascii... ($cmd)\n"
eval "$cmd"
ascii_size=`calcsize $PACKAGE.txt`
gzip -f -9 -c $PACKAGE.txt >"$outdir/$PACKAGE.txt.gz"
ascii_gz_size=`calcsize "$outdir/$PACKAGE.txt.gz"`
mv $PACKAGE.txt "$outdir/"
ls -l "$outdir/$PACKAGE.txt" "$outdir/$PACKAGE.txt.gz"
fi
#
if $generate_html; then
# Split HTML at level $1. Used for texi2html.
html_split()
{
opt="--split=$1 --node-files $commonarg $htmlarg"
cmd="$SETLANG $TEXI2HTML --output $PACKAGE.html $opt \"$srcfile\""
printf "\nGenerating html by $1... ($cmd)\n"
eval "$cmd"
split_html_dir=$PACKAGE.html
(
cd ${split_html_dir} || exit 1
if [ ! -f index.html ]; then
ln -sf ${PACKAGE}.html index.html
fi
tar -czf "$abs_outdir/${PACKAGE}.html_$1.tar.gz" -- *.html
)
eval html_$1_tgz_size=`calcsize "$outdir/${PACKAGE}.html_$1.tar.gz"`
rm -f "$outdir"/html_$1/*.html
mkdir -p "$outdir/html_$1/"
mv ${split_html_dir}/*.html "$outdir/html_$1/"
rmdir ${split_html_dir}
}
if test -z "$use_texi2html"; then
opt="--no-split --html -o $PACKAGE.html $commonarg $htmlarg"
cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
printf "\nGenerating monolithic html... ($cmd)\n"
rm -rf $PACKAGE.html # in case a directory is left over
eval "$cmd"
html_mono_size=`calcsize $PACKAGE.html`
gzip -f -9 -c $PACKAGE.html >"$outdir/$PACKAGE.html.gz"
html_mono_gz_size=`calcsize "$outdir/$PACKAGE.html.gz"`
copy_images "$outdir/" $PACKAGE.html
mv $PACKAGE.html "$outdir/"
ls -l "$outdir/$PACKAGE.html" "$outdir/$PACKAGE.html.gz"
# Before Texinfo 5.0, makeinfo did not accept a --split=HOW option,
# it just always split by node. So if we're splitting by node anyway,
# leave it out.
if test "x$split" = xnode; then
split_arg=
else
split_arg=--split=$split
fi
#
opt="--html -o $PACKAGE.html $split_arg $commonarg $htmlarg"
cmd="$SETLANG $MAKEINFO $opt \"$srcfile\""
printf "\nGenerating html by $split... ($cmd)\n"
eval "$cmd"
split_html_dir=$PACKAGE.html
copy_images $split_html_dir/ $split_html_dir/*.html
(
cd $split_html_dir || exit 1
tar -czf "$abs_outdir/$PACKAGE.html_$split.tar.gz" -- *
)
eval \
html_${split}_tgz_size=`calcsize "$outdir/$PACKAGE.html_$split.tar.gz"`
rm -rf "$outdir/html_$split/"
mv $split_html_dir "$outdir/html_$split/"
du -s "$outdir/html_$split/"
ls -l "$outdir/$PACKAGE.html_$split.tar.gz"
else # use texi2html:
opt="--output $PACKAGE.html $commonarg $htmlarg"
cmd="$SETLANG $TEXI2HTML $opt \"$srcfile\""
printf "\nGenerating monolithic html with texi2html... ($cmd)\n"
rm -rf $PACKAGE.html # in case a directory is left over
eval "$cmd"
html_mono_size=`calcsize $PACKAGE.html`
gzip -f -9 -c $PACKAGE.html >"$outdir/$PACKAGE.html.gz"
html_mono_gz_size=`calcsize "$outdir/$PACKAGE.html.gz"`
mv $PACKAGE.html "$outdir/"
html_split node
html_split chapter
html_split section
fi
fi # end html
#
printf "\nMaking .tar.gz for sources...\n"
d=`dirname $srcfile`
(
cd "$d"
srcfiles=`ls -d *.texinfo *.texi *.txi *.eps $source_extra 2>/dev/null` || true
tar czfh "$abs_outdir/$PACKAGE.texi.tar.gz" $srcfiles
ls -l "$abs_outdir/$PACKAGE.texi.tar.gz"
)
texi_tgz_size=`calcsize "$outdir/$PACKAGE.texi.tar.gz"`
#
# Do everything again through docbook.
if test -n "$docbook"; then
opt="-o - --docbook $commonarg"
cmd="$SETLANG $MAKEINFO $opt \"$srcfile\" >${srcdir}/$PACKAGE-db.xml"
printf "\nGenerating docbook XML... ($cmd)\n"
eval "$cmd"
docbook_xml_size=`calcsize $PACKAGE-db.xml`
gzip -f -9 -c $PACKAGE-db.xml >"$outdir/$PACKAGE-db.xml.gz"
docbook_xml_gz_size=`calcsize "$outdir/$PACKAGE-db.xml.gz"`
mv $PACKAGE-db.xml "$outdir/"
split_html_db_dir=html_node_db
opt="$commonarg -o $split_html_db_dir"
cmd="$DOCBOOK2HTML $opt \"${outdir}/$PACKAGE-db.xml\""
printf "\nGenerating docbook HTML... ($cmd)\n"
eval "$cmd"
(
cd ${split_html_db_dir} || exit 1
tar -czf "$abs_outdir/${PACKAGE}.html_node_db.tar.gz" -- *.html
)
html_node_db_tgz_size=`calcsize "$outdir/${PACKAGE}.html_node_db.tar.gz"`
rm -f "$outdir"/html_node_db/*.html
mkdir -p "$outdir/html_node_db"
mv ${split_html_db_dir}/*.html "$outdir/html_node_db/"
rmdir ${split_html_db_dir}
cmd="$DOCBOOK2TXT \"${outdir}/$PACKAGE-db.xml\""
printf "\nGenerating docbook ASCII... ($cmd)\n"
eval "$cmd"
docbook_ascii_size=`calcsize $PACKAGE-db.txt`
mv $PACKAGE-db.txt "$outdir/"
cmd="$DOCBOOK2PDF \"${outdir}/$PACKAGE-db.xml\""
printf "\nGenerating docbook PDF... ($cmd)\n"
eval "$cmd"
docbook_pdf_size=`calcsize $PACKAGE-db.pdf`
mv $PACKAGE-db.pdf "$outdir/"
fi
#
printf "\nMaking index.html for $PACKAGE...\n"
if test -z "$use_texi2html"; then
CONDS="/%%IF *HTML_SECTION%%/,/%%ENDIF *HTML_SECTION%%/d;\
/%%IF *HTML_CHAPTER%%/,/%%ENDIF *HTML_CHAPTER%%/d"
else
# should take account of --split here.
CONDS="/%%ENDIF.*%%/d;/%%IF *HTML_SECTION%%/d;/%%IF *HTML_CHAPTER%%/d"
fi
curdate=`$SETLANG date '+%B %d, %Y'`
sed \
-e "s!%%TITLE%%!$MANUAL_TITLE!g" \
-e "s!%%EMAIL%%!$EMAIL!g" \
-e "s!%%PACKAGE%%!$PACKAGE!g" \
-e "s!%%DATE%%!$curdate!g" \
-e "s!%%HTML_MONO_SIZE%%!$html_mono_size!g" \
-e "s!%%HTML_MONO_GZ_SIZE%%!$html_mono_gz_size!g" \
-e "s!%%HTML_NODE_TGZ_SIZE%%!$html_node_tgz_size!g" \
-e "s!%%HTML_SECTION_TGZ_SIZE%%!$html_section_tgz_size!g" \
-e "s!%%HTML_CHAPTER_TGZ_SIZE%%!$html_chapter_tgz_size!g" \
-e "s!%%INFO_TGZ_SIZE%%!$info_tgz_size!g" \
-e "s!%%DVI_GZ_SIZE%%!$dvi_gz_size!g" \
-e "s!%%PDF_SIZE%%!$pdf_size!g" \
-e "s!%%ASCII_SIZE%%!$ascii_size!g" \
-e "s!%%ASCII_GZ_SIZE%%!$ascii_gz_size!g" \
-e "s!%%TEXI_TGZ_SIZE%%!$texi_tgz_size!g" \
-e "s!%%DOCBOOK_HTML_NODE_TGZ_SIZE%%!$html_node_db_tgz_size!g" \
-e "s!%%DOCBOOK_ASCII_SIZE%%!$docbook_ascii_size!g" \
-e "s!%%DOCBOOK_PDF_SIZE%%!$docbook_pdf_size!g" \
-e "s!%%DOCBOOK_XML_SIZE%%!$docbook_xml_size!g" \
-e "s!%%DOCBOOK_XML_GZ_SIZE%%!$docbook_xml_gz_size!g" \
-e "s,%%SCRIPTURL%%,$scripturl,g" \
-e "s!%%SCRIPTNAME%%!$prog!g" \
-e "$CONDS" \
$GENDOCS_TEMPLATE_DIR/gendocs_template >"$outdir/index.html"
echo "Done, see $outdir/ subdirectory for new files."
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

101
doc/gendocs_template Normal file
View File

@ -0,0 +1,101 @@
<!--#include virtual="/server/header.html" -->
<!-- Parent-Version: 1.78 -->
<!--
Copyright (C) 2006-2023 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
-->
<title>%%TITLE%% - GNU Project - Free Software Foundation</title>
<!--#include virtual="/server/banner.html" -->
<h2>%%TITLE%%</h2>
<address>Free Software Foundation</address>
<address>last updated %%DATE%%</address>
<p>This manual (%%PACKAGE%%) is available in the following formats:</p>
<ul>
<li><a href="%%PACKAGE%%.html">HTML
(%%HTML_MONO_SIZE%%K bytes)</a> - entirely on one web page.</li>
<li><a href="html_node/index.html">HTML</a> - with one web page per
node.</li>
%%IF HTML_SECTION%%
<li><a href="html_section/index.html">HTML</a> - with one web page per
section.</li>
%%ENDIF HTML_SECTION%%
%%IF HTML_CHAPTER%%
<li><a href="html_chapter/index.html">HTML</a> - with one web page per
chapter.</li>
%%ENDIF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.html.gz">HTML compressed
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
one web page.</li>
<li><a href="%%PACKAGE%%.html_node.tar.gz">HTML compressed
(%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per node.</li>
%%IF HTML_SECTION%%
<li><a href="%%PACKAGE%%.html_section.tar.gz">HTML compressed
(%%HTML_SECTION_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per section.</li>
%%ENDIF HTML_SECTION%%
%%IF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.html_chapter.tar.gz">HTML compressed
(%%HTML_CHAPTER_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per chapter.</li>
%%ENDIF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.info.tar.gz">Info document
(%%INFO_TGZ_SIZE%%K bytes gzipped tar file)</a>.</li>
<li><a href="%%PACKAGE%%.txt">ASCII text
(%%ASCII_SIZE%%K bytes)</a>.</li>
<li><a href="%%PACKAGE%%.txt.gz">ASCII text compressed
(%%ASCII_GZ_SIZE%%K bytes gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.dvi.gz">TeX dvi file
(%%DVI_GZ_SIZE%%K bytes gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.pdf">PDF file
(%%PDF_SIZE%%K bytes)</a>.</li>
<li><a href="%%PACKAGE%%.texi.tar.gz">Texinfo source
(%%TEXI_TGZ_SIZE%%K bytes gzipped tar file).</a></li>
</ul>
<p>You can <a href="https://shop.fsf.org/">buy printed copies of
some manuals</a> (among other items) from the Free Software Foundation;
this helps support FSF activities.</p>
<p>(This page generated by the <a href="%%SCRIPTURL%%">%%SCRIPTNAME%%
script</a>.)</p>
<!-- If needed, change the copyright block at the bottom. In general,
all pages on the GNU web server should have the section about
verbatim copying. Please do NOT remove this without talking
with the webmasters first.
Please make sure the copyright date is consistent with the document
and that it is like this: "2001, 2002", not this: "2001-2002". -->
</div><!-- for id="content", starts in the include above -->
<!--#include virtual="/server/footer.html" -->
<div id="footer">
<div class="unprintable">
<p>Please send general FSF &amp; GNU inquiries to
<a href="mailto:gnu@gnu.org">&lt;gnu@gnu.org&gt;</a>.
There are also <a href="/contact/">other ways to contact</a>
the FSF. Broken links and other corrections or suggestions can be sent
to <a href="mailto:%%EMAIL%%">&lt;%%EMAIL%%&gt;</a>.</p>
</div>
<p>Copyright &copy; 2020 Free Software Foundation, Inc.</p>
<p>This page is licensed under a <a rel="license"
href="https://creativecommons.org/licenses/by-nd/3.0/us/">Creative
Commons Attribution-NoDerivs 3.0 United States License</a>.</p>
<!--#include virtual="/server/bottom-notes.html" -->
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2006--2026 Free Software Foundation, Inc.
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.

View File

@ -1,6 +1,6 @@
;;; mastermenu.el --- Redefinition of texinfo-master-menu-list
;; Copyright 2006-2026 Free Software Foundation, Inc.
;; Copyright 2006-2023 Free Software Foundation, Inc.
;; Author: Sergey Poznyakoff
;; Maintainer: bug-tar@gnu.org

View File

@ -1,5 +1,5 @@
@c This is part of the GNU tar manual.
@c Copyright (C) 2017--2026 Free Software Foundation, Inc.
@c Copyright (C) 2017--2023 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.3 or any later version
@c published by the Free Software Foundation.
@ -127,7 +127,7 @@ foo/bar/file
@end example
Supposing its name is @file{file.list} and the script is named
@file{restore.sh}, you can invoke it as follows:
@file{restore.sh}, you can invoke it as follows:
@example
# @kbd{sh restore.sh A.tar file.list}

View File

@ -1,5 +1,5 @@
@c This is part of GNU tar manual.
@c Copyright 1992--2026 Free Software Foundation, Inc.
@c Copyright 1992--2023 Free Software Foundation, Inc.
@c See file tar.texi for copying conditions.
@c This file contains support for 'renditions' by Fran@,{c}ois Pinard

View File

@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2005--2026 Free Software Foundation, Inc.
@c Copyright (C) 2005--2023 Free Software Foundation, Inc.
@c Written by Sergey Poznyakoff
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.
@ -11,7 +11,7 @@ used to determine which files were modified since the last backup.
@GNUTAR{} version @value{VERSION} supports three snapshot file
formats. The first format, called @dfn{format 0}, is the one used by
@GNUTAR{} versions up to and including 1.15.1. The second format, called
@GNUTAR{} versions up to and including 1.15.1. The second format, called
@dfn{format 1} is an extended version of this format, that contains more
metadata and allows for further extensions. It was used by alpha release
version 1.15.90. For alpha version 1.15.91 and stable releases
@ -42,7 +42,7 @@ where:
@table @var
@item nfs
A single plus character (@samp{+}), if this directory is located on
an @acronym{NFS}-mounted partition, otherwise empty.
an @acronym{NFS}-mounted partition, otherwise empty.
(That is, for non-NFS directories, the first character on the
description line contains the start of the @var{dev} field.)

View File

@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2006--2026 Free Software Foundation, Inc.
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.

View File

@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2007--2026 Free Software Foundation, Inc.
@c Copyright (C) 2007--2023 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.
@ -10,7 +10,7 @@
kernel version, reconfiguring your hardware, loading kernel modules in a
different order, using virtual volumes that are assembled dynamically
(such as with @acronym{LVM} or @acronym{RAID}), hot-plugging drives
(e.g. external USB or Firewire drives), etc. In the majority of
(e.g. external USB or Firewire drives), etc. In the majority of
cases this change is unnoticed by the users. However, it influences
@command{tar} incremental backups: the device number is stored in tar
snapshot files (@pxref{Snapshot Files}) and is used to determine whether
@ -35,7 +35,7 @@ $ @kbd{tar-snapshot-edit @var{snapfile}}
@noindent
where @var{snapfile} is the name of the snapshot file (you can supply as many
files as you wish in a single command line). You can then compare the
files as you wish in a single command line). You can then compare the
numbers across snapshot files, or against those currently in use on the
live filesystem (using @command{ls -l} or @command{stat}).
@ -44,7 +44,7 @@ to simply tell @GNUTAR{} to ignore the device number when processing the
incremental snapshot files for these backups, using the
@option{--no-check-device} option (@pxref{device numbers}).
Alternatively, you can use the @command{tar-edit-snapshot} script's
Alternatively, you can use the @command{tar-edit-snapshot} script's
@option{-r} option to update all occurrences of the given device
number in the snapshot file(s). It takes a single argument
of the form

View File

@ -1,4 +1,5 @@
.\" This file is part of GNU tar. -*- nroff -*-
.\" Copyright 2013-2023 Free Software Foundation, Inc.
.\"
.\" GNU tar is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@ -12,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.TH TAR 1 "November 13, 2025" "TAR" "GNU TAR Manual"
.TH TAR 1 "July 11, 2022" "TAR" "GNU TAR Manual"
.SH NAME
tar \- an archiving utility
.SH SYNOPSIS
@ -20,6 +21,7 @@ tar \- an archiving utility
\fBtar\fR {\fBA\fR|\fBc\fR|\fBd\fR|\fBr\fR|\fBt\fR|\fBu\fR|\fBx\fR}\
[\fBGnSkUWOmpsMBiajJzZhPlRvwo\fR] [\fIARG\fR...]
.SS UNIX-style usage
.sp
\fBtar\fR \fB\-A\fR [\fIOPTIONS\fR] \fB\-f\fR \fIARCHIVE\fR \fIARCHIVE\fR...
.sp
\fBtar\fR \fB\-c\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
@ -34,6 +36,7 @@ tar \- an archiving utility
.sp
\fBtar\fR \fB\-x\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.SS GNU-style usage
.sp
\fBtar\fR {\fB\-\-catenate\fR|\fB\-\-concatenate\fR} [\fIOPTIONS\fR] \fB\-\-file\fR \fIARCHIVE\fR \fIARCHIVE\fR...
.sp
\fBtar\fR \fB\-\-create\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
@ -80,9 +83,11 @@ GNU
.B tar
is an archiving program designed to store multiple files in a single
file (an \fBarchive\fR), and to manipulate such archives. The archive
can be either a regular file or a device (e.g., a tape drive, hence the name
can be either a regular file or a device (e.g. a tape drive, hence the name
of the program, which stands for \fBt\fRape \fBar\fRchiver), which can
be located either on the local or on a remote machine.
.PP
.SS Option styles
Options to GNU \fBtar\fR can be given in three different styles.
In
@ -117,20 +122,20 @@ argument must follow the option letter without any intervening
whitespace, as in \fB\-g/tmp/snar.db\fR.
.PP
Any number of options not taking arguments can be
clustered together after a single dash, e.g.\& \fB\-vkp\fR. An option
clustered together after a single dash, e.g. \fB\-vkp\fR. An option
that takes an argument (whether mandatory or optional) can appear at
the end of such a cluster, e.g.\& \fB\-vkpf a.tar\fR.
the end of such a cluster, e.g. \fB\-vkpf a.tar\fR.
.PP
The example command above written in the
.B short-option style
could look like:
.PP
.EX
tar \-cvf etc.tar /etc
tar -cvf etc.tar /etc
.EE
or
.EX
tar \-c \-v \-f etc.tar /etc
tar -c -v -f etc.tar /etc
.EE
.PP
In
@ -147,11 +152,11 @@ method.
Here are several ways of writing the example command in this style:
.PP
.EX
tar \-\-create \-\-file etc.tar \-\-verbose /etc
tar --create --file etc.tar --verbose /etc
.EE
or (abbreviating some options):
.EX
tar \-\-cre \-\-file=etc.tar \-\-verb /etc
tar --cre --file=etc.tar --verb /etc
.EE
.PP
The options in all three styles can be intermixed, although doing so
@ -182,7 +187,7 @@ archived. Directories are archived recursively, unless the
\fB\-d\fR, \fB\-\-diff\fR, \fB\-\-compare\fR
Find differences between archive and file system. The arguments are
optional and specify archive members to compare. If not given, the
working directory is assumed.
current working directory is assumed.
.TP
\fB\-\-delete\fR
Delete from the archive. The arguments supply names of the archive
@ -223,6 +228,7 @@ same name, corresponding to various versions of the same file.
Extract files from an archive. Arguments are optional. When given,
they specify names of the archive members to be extracted.
.TP
.TP
\fB\-\-show\-defaults\fR
Show built-in defaults for various \fBtar\fR options and exit.
.TP
@ -275,7 +281,7 @@ the snapshot file before dumping, thereby forcing a level 0 dump.
Assume the archive is seekable. Normally \fBtar\fR determines
automatically whether the archive can be seeked or not. This option
is intended for use in cases when such recognition fails. It takes
effect only if the archive is open for reading (e.g., with
effect only if the archive is open for reading (e.g. with
.B \-\-list
or
.B \-\-extract
@ -441,7 +447,7 @@ GNU \fBtar\fR version number.
The name of the archive \fBtar\fR is processing.
.TP
.B TAR_BLOCKING_FACTOR
Current blocking factor, i.e., number of 512-byte blocks in a record.
Current blocking factor, i.e. number of 512-byte blocks in a record.
.TP
.B TAR_VOLUME
Ordinal number of the volume \fBtar\fR is processing (set if
@ -591,7 +597,7 @@ Disable POSIX ACLs support.
.B \-\-selinux
Enable SELinux context support.
.TP
.B \-\-no\-selinux
.B \-\-no-selinux
Disable SELinux context support.
.TP
.B \-\-xattrs
@ -602,7 +608,7 @@ Disable extended attributes support.
.TP
.BI \-\-xattrs\-exclude= PATTERN
Specify the exclude pattern for xattr keys. \fIPATTERN\fR is a globbing
pattern, e.g.\& \fB\-\-xattrs\-exclude='user.*'\fR to include only
pattern, e.g. \fB\-\-xattrs\-exclude='user.*'\fR to include only
attributes from the user namespace.
.TP
.BI \-\-xattrs\-include= PATTERN
@ -625,7 +631,7 @@ name or IP address, and the part after it as the file or device
pathname, e.g.:
.EX
\-\-file=remotehost:/dev/sr0
--file=remotehost:/dev/sr0
.EE
An optional username can be prefixed to the hostname, placing a \fB@\fR
@ -638,7 +644,7 @@ command. Nowadays it is common to use
instead. You can do so by giving the following command line option:
.EX
\-\-rsh-command=/usr/bin/ssh
--rsh-command=/usr/bin/ssh
.EE
The remote machine should have the
@ -664,7 +670,7 @@ GNU \fBtar\fR version number.
The name of the archive \fBtar\fR is processing.
.TP
.B TAR_BLOCKING_FACTOR
Current blocking factor, i.e., number of 512-byte blocks in a record.
Current blocking factor, i.e. number of 512-byte blocks in a record.
.TP
.B TAR_VOLUME
Ordinal number of the volume \fBtar\fR is processing (set if
@ -742,7 +748,7 @@ reading archives created with the \fB\-A\fR option.
\fB\-\-record\-size\fR=\fINUMBER\fR
Set record size. \fINUMBER\fR is the number of bytes per record. It
must be multiple of \fB512\fR. It can can be suffixed with a \fBsize
suffix\fR, e.g.\& \fB\-\-record-size=10K\fR, for 10 Kilobytes. See the
suffix\fR, e.g. \fB\-\-record-size=10K\fR, for 10 Kilobytes. See the
subsection
.BR "Size suffixes" ,
for a list of valid suffixes.
@ -848,15 +854,17 @@ Make numbered backups if numbered backups exist, simple backups otherwise.
.TP
.BR never ", " simple
Always make simple backups
.RS
.RE
.IP
If \fICONTROL\fR is not given, the value is taken from the
.B VERSION_CONTROL
environment variable. If it is not set, \fBexisting\fR is assumed.
.RE
.TP
\fB\-C\fR, \fB\-\-directory\fR=\fIDIR\fR
Change to \fIDIR\fR before performing any operations. This option is
order-sensitive, i.e., it affects all options that follow.
order-sensitive, i.e. it affects all options that follow.
.TP
\fB\-\-exclude\fR=\fIPATTERN\fR
Exclude files matching \fIPATTERN\fR, a
@ -868,15 +876,7 @@ Exclude backup and lock files.
.TP
\fB\-\-exclude\-caches\fR
Exclude contents of directories containing file \fBCACHEDIR.TAG\fR,
except for the tag file itself. The \fBCACHEDIR.TAG\fR file must be
a regular file whose content begins with the following 43 characters:
.IP
.RS
.EX
Signature: 8a477f597d28d172789f06886806bc55
.EE
.RE
except for the tag file itself.
.TP
\fB\-\-exclude\-caches\-all\fR
Exclude directories containing file \fBCACHEDIR.TAG\fR and the file itself.
@ -981,7 +981,7 @@ unless overridden by environment variable \fBSIMPLE_BACKUP_SUFFIX\fR.
Get names to extract or create from \fIFILE\fR.
Unless specified otherwise, the \fIFILE\fR must contain a list of
names separated by ASCII \fBLF\fR (i.e., one name per line). The
names separated by ASCII \fBLF\fR (i.e. one name per line). The
names read are handled the same way as command line arguments. They
undergo quote removal and word splitting, and any string that starts
with a \fB\-\fR is handled as \fBtar\fR command line option.
@ -1004,7 +1004,7 @@ Treat each line obtained from a file list as a file name, even if it
starts with a dash. File lists are supplied with the
\fB\-\-files\-from\fR (\fB\-T\fR) option. The default behavior is to
handle names supplied in file lists as if they were typed in the
command line, i.e., any names starting with a dash are treated as
command line, i.e. any names starting with a dash are treated as
\fBtar\fR options. The \fB\-\-verbatim\-files\-from\fR option
disables this behavior.
@ -1144,7 +1144,7 @@ Disable all warning messages.
.B alone-zero-block
"A lone zero block at %s"
.HP
Keywords applicable for \fBtar \-\-create\fR:
Keywords applicable for \fBtar --create\fR:
.TP
.B cachedir
"%s: contains a cache directory tag %s; %s"
@ -1180,7 +1180,7 @@ keyword applies only if used together with the
.B \-\-ignore\-failed\-read
option.
.HP
Keywords applicable for \fBtar \-\-extract\fR:
Keywords applicable for \fBtar --extract\fR:
.TP
.B existing\-file
"%s: skipping existing file"
@ -1212,7 +1212,7 @@ default (unless \fB\-\-verbose\fR is used). A common example of what
you can get when using this warning is:
.EX
$ tar \-\-warning=decompress-program \-x \-f archive.Z
$ tar --warning=decompress-program -x -f archive.Z
tar (child): cannot run compress: No such file or directory
tar (child): trying gzip
.EE
@ -1249,6 +1249,7 @@ Ask for confirmation for every action.
When creating, same as \fB\-\-old\-archive\fR. When extracting, same
as \fB\-\-no\-same\-owner\fR.
.SS Size suffixes
.sp
.nf
.ta 8n 18n 42n
.ul
@ -1264,6 +1265,7 @@ as \fB\-\-no\-same\-owner\fR.
T Terabytes \fISIZE\fR x 1024^4
w Words \fISIZE\fR x 2
.fi
.PP
.SH "RETURN VALUE"
Tar's exit code indicates whether it was able to successfully perform
the requested operation, and if not, what kind of error occurred.
@ -1275,11 +1277,10 @@ Successful termination.
.I Some files differ.
If \fBtar\fR was invoked with the \fB\-\-compare\fR (\fB\-\-diff\fR, \fB\-d\fR)
command line option, this means that some files in the archive differ
from their disk counterparts. If \fBtar\fR was given one of the
\fB\-\-create\fR, \fB\-\-append\fR or \fB\-\-update\fR options, this
exit code means that some files were changed while being archived and
so the resulting archive does not contain the exact copy of the file
set.
from their disk counterparts. If \fBtar\fR was given one of the \fB\-\-create\fR,
\fB\-\-append\fR or \fB\-\-update\fR options, this exit code means
that some files were changed while being archived and so the resulting
archive does not contain the exact copy of the file set.
.TP
.B 2
.I Fatal error.
@ -1290,7 +1291,7 @@ If a subprocess that had been invoked by
exited with a nonzero exit code,
.B tar
itself exits with that code as well. This can happen, for example, if
a compression option (e.g.\& \fB\-z\fR) was used and the external
a compression option (e.g. \fB\-z\fR) was used and the external
compressor program failed. Another example is
.B rmt
failure during backup to a remote device.
@ -1319,7 +1320,7 @@ found at:
.SH "BUG REPORTS"
Report bugs to <bug\-tar@gnu.org>.
.SH COPYRIGHT
Copyright \(co 2013\(en2026 Free Software Foundation, Inc.
Copyright \(co 2023 Free Software Foundation, Inc.
.br
.na
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

View File

@ -47,7 +47,7 @@ This manual is for @acronym{GNU} @command{tar} (version
from archives.
Copyright @copyright{} 1992, 1994--1997, 1999--2001, 2003--2017,
2021--2026 Free Software Foundation, Inc.
2021--2023 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
@ -346,7 +346,6 @@ Controlling the Archive Format
* Compression:: Using Less Space through Compression
* Attributes:: Handling File Attributes
* Portability:: Making @command{tar} Archives More Portable
* Reproducibility:: Making @command{tar} Archives More Reproducible
* cpio:: Comparison of @command{tar} and @command{cpio}
Using Less Space through Compression
@ -1019,7 +1018,7 @@ The full output consists of six fields:
@item File type and permissions in symbolic form.
These are displayed in the same format as the first column of
@command{ls -l} output (@pxref{What information is listed,
format=verbose, Verbose listing, coreutils, GNU core utilities}).
format=verbose, Verbose listing, fileutils, GNU file utilities}).
@item Owner name and group separated by a slash character.
If these data are not available (for example, when listing a @samp{v7} format
@ -1399,7 +1398,7 @@ practice/collection.tar
@end smallexample
Note that the archive thus created is not in the subdirectory
@file{practice}, but rather in the working directory---the
@file{practice}, but rather in the current working directory---the
directory from which @command{tar} was invoked. Before trying to archive a
directory from its superior directory, you should make sure you have
write access to the superior directory itself, not only the directory
@ -1421,7 +1420,7 @@ $ @kbd{tar --create --file=foo.tar .}
@noindent
@command{tar} will report @samp{tar: ./foo.tar is the archive; not
dumped}. This happens because @command{tar} creates the archive
@file{foo.tar} in the working directory before putting any files into
@file{foo.tar} in the current directory before putting any files into
it. Then, when @command{tar} attempts to add all the files in the
directory @file{.} to the archive, it notices that the file
@file{./foo.tar} is the same as the archive @file{foo.tar}, and skips
@ -1849,7 +1848,7 @@ $ @kbd{tar --extract --file=music.tar --strip-components=1 folk}
@end smallexample
@noindent
will extract the file @file{folk} into the working directory.
will extract the file @file{folk} into the current working directory.
@node going further
@section Going Further Ahead in this Manual
@ -2662,14 +2661,13 @@ directories until the end of extraction. @xref{Directory Modification Times and
When reading or writing a file to be archived, @command{tar} accesses
the file that a symbolic link points to, rather than the symlink
itself. This a dangerous option, as it can cause @command{tar} to
access files outside the working directory. @xref{dereference}.
itself. @xref{dereference}.
@opsummary{directory}
@item --directory=@var{dir}
@itemx -C @var{dir}
When this option is specified, @command{tar} will change its working directory
When this option is specified, @command{tar} will change its current directory
to @var{dir} before performing any operations. When this option is used
during archive creation, it is order sensitive. @xref{directory}.
@ -2722,7 +2720,7 @@ The patterns affect only the directory itself. @xref{exclude}.
@item --exclude-ignore-recursive=@var{file}
Before dumping a directory, @command{tar} checks if it contains
@var{file}. If so, exclusion patterns are read from this file.
The patterns affect the directory and all its subdirectories.
The patterns affect the directory and all itssubdirectories.
@xref{exclude}.
@opsummary{exclude-tag}
@ -2808,7 +2806,7 @@ numeric fields.
Creates a @acronym{POSIX.1-1988} compatible archive.
@item posix
Creates a @acronym{POSIX.1-2001} archive.
Creates a @acronym{POSIX.1-2001 archive}.
@end table
@ -3050,8 +3048,8 @@ latter case, the modification time of that file is used. @xref{override}.
When @command{--clamp-mtime} is also specified, files with
modification times earlier than @var{date} will retain their actual
modification times, and @var{date} will be used only for files with
modification times later than @var{date}.
modification times, and @var{date} will only be used for files whose
modification times are later than @var{date}.
@opsummary{multi-volume}
@item --multi-volume
@ -3268,20 +3266,16 @@ Synonym for @option{--format=v7}.
@opsummary{one-file-system}
@item --one-file-system
Used when creating an archive. Prevents @command{tar} from recursing into
directories that are on different file systems from the working
directories that are on different file systems from the current
directory.
@opsummary{one-top-level}
@item --one-top-level[=@var{dir}]
Tells @command{tar} to create a new directory beneath the extraction directory
(or the one passed to @option{-C}) and use it to prevent @command{tar}
from modifying files outside that directory.
If @var{dir} is present, it must be a relative file name.
If it is absent, the name of the new directory
is the base name of the archive minus any recognized archive suffix.
If multiple @option{-C} options are present,
each has its own subdirectory with the same name.
Any member names that do not begin
(or the one passed to @option{-C}) and use it to guard against
tarbombs. In the absence of @var{dir} argument, the name of the new directory
will be equal to the base name of the archive (file name minus the
archive suffix, if recognized). Any member names that do not begin
with that directory name (after
transformations from @option{--transform} and
@option{--strip-components}) will be prefixed with it. Recognized
@ -3531,7 +3525,7 @@ No directory sorting is performed. This is the default.
@item name
Sort the directory entries on name. The operating system may deliver
directory entries in a more or less random order, and sorting them
makes archive creation more reproducible. @xref{Reproducibility}.
makes archive creation reproducible.
@item inode
Sort the directory entries on inode number. Sorting directories on
@ -3641,7 +3635,7 @@ $ @kbd{tar cf archive.tar --transform 's,^\./,usr/,' .}
@end smallexample
@noindent
will add to @file{archive} files from the working directory,
will add to @file{archive} files from the current working directory,
replacing initial @samp{./} prefix with @samp{usr/}. For the detailed
discussion, @xref{transform}.
@ -3919,7 +3913,7 @@ and @command{tar} were invoked as follows:
@end example
@noindent
then the file @file{README} would be looked up in the working
then the file @file{README} would be looked up in the current working
directory, and files @file{main.c} and @file{Makefile} would be looked
up in the directory @file{src}.
@ -4033,7 +4027,7 @@ successfully. For example, @w{@samp{tar --version}} might print:
@smallexample
tar (GNU tar) @value{VERSION}
Copyright (C) 2026 Free Software Foundation, Inc.
Copyright (C) 2013-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
@ -4220,11 +4214,6 @@ Total bytes read: 7924664320 (7.4GiB, 95MiB/s)
@end group
@end smallexample
Notice, that since @command{tar} operates on @dfn{records}, the number
of bytes reported can be rounded up to the nearest full record. This
can happen, in particular, when the last record in the archive is
partial. @xref{Blocking}.
Finally, when deleting from an archive, the @option{--totals} option
displays both numbers plus number of bytes removed from the archive:
@ -4644,7 +4633,7 @@ The subsections below discuss allowed values for @var{keyword} along with the
warning messages they control.
@menu
* General Warnings:: Keywords controlling @command{tar} operation.
* General Warnings:: Keywords applicable for @command{tar --create}.
* Archive Creation Warnings:: Keywords applicable for @command{tar --create}.
* Archive Extraction Warnings:: Keywords applicable for @command{tar --extract}.
* Incremental Extraction Warnings:: Keywords controlling incremental extraction.
@ -4675,11 +4664,6 @@ suppressed if @option{--ignore-zeros} is in effect (@pxref{Ignore
Zeros}).
@end defvr
@defvr {warning} empty-transform
@cindex @samp{transforms to empty name}, warning message.
@samp{transforms to empty name}. @xref{transform}.
@end defvr
@defvr {warning} missing-zero-blocks
@cindex @samp{Terminating zero blocks missing}, warning message.
@samp{Terminating zero blocks missing at %s}. This warning is
@ -4978,7 +4962,7 @@ Books, ISBN 1-56884-203-1.}. The two most common errors are:
@enumerate
@item
Mistakenly using @code{create} instead of @code{extract}, when the
Mistakingly using @code{create} instead of @code{extract}, when the
intent was to extract the full contents of an archive. This error
is likely: keys @kbd{c} and @kbd{x} are right next to each other on
the QWERTY keyboard. Instead of being unpacked, the archive then
@ -5594,7 +5578,7 @@ When adding files to an archive, @command{tar} will use
from the files. @var{permissions} can be specified either as an octal
number or as symbolic permissions, like with
@command{chmod} (@xref{File permissions, Permissions, File
permissions, coreutils, @acronym{GNU} core utilities}. This reference
permissions, fileutils, @acronym{GNU} file utilities}. This reference
also has useful information for those not being overly familiar with
the UNIX permission system). Using latter syntax allows for
more flexibility. For example, the value @samp{a+rw} adds read and write
@ -5608,27 +5592,28 @@ $ @kbd{tar -c -f archive.tar --mode='a+rw' .}
@item --mtime=@var{date}
@opindex mtime
When adding files to an archive, @command{tar} uses @var{date} as
When adding files to an archive, @command{tar} will use @var{date} as
the modification time of members when creating archives, instead of
their actual modification times. The argument @var{date} can be
either a textual date representation in almost arbitrary format
(@pxref{Date input formats}) or a name of an existing file, starting
with @samp{/} or @samp{.}. In the latter case, the modification time
of that file is used.
of that file will be used.
The following example sets the modification date to 00:00:00 @sc{utc} on
The following example will set the modification date to 00:00:00,
January 1, 1970:
@smallexample
$ @kbd{tar -c -f archive.tar --mtime='@@0' .}
$ @kbd{tar -c -f archive.tar --mtime='1970-01-01' .}
@end smallexample
@noindent
When used with @option{--verbose} (@pxref{verbose tutorial}) @GNUTAR{}
converts the specified date back to a textual form and compares it
with the one given with @option{--mtime}.
If the two forms differ, @command{tar} prints both forms in a message,
to help the user check that the right date is being used.
will try to convert the specified date back to its textual
representation and compare it with the one given with
@option{--mtime} options. If the two dates differ, @command{tar} will
print a warning saying what date it will use. This is to help user
ensure he is using the right date.
For example:
@ -5640,15 +5625,14 @@ tar: Option --mtime: Treating date 'yesterday' as 2006-06-20
@end smallexample
@noindent
When used with @option{--clamp-mtime} @GNUTAR{} sets the
modification date to @var{date} only on files whose actual modification
date is later than @var{date}. This makes it easier to build
When used with @option{--clamp-mtime} @GNUTAR{} will only set the
modification date to @var{date} on files whose actual modification
date is later than @var{date}. This is to make it easy to build
reproducible archives given a common timestamp for generated files
while still retaining the original timestamps of untouched files.
@xref{Reproducibility}.
@smallexample
$ @kbd{tar -c -f archive.tar --clamp-mtime --mtime="$SOURCE_EPOCH" .}
$ @kbd{tar -c -f archive.tar --clamp-mtime --mtime=@@$SOURCE_DATE_EPOCH .}
@end smallexample
@item --owner=@var{user}
@ -7826,7 +7810,7 @@ If you specify either @option{--list} (@option{-t}) or
operates on all the archive members in the archive.
If run with @option{--diff} option, tar will compare the archive with
the contents of the working directory.
the contents of the current working directory.
If you specify any other operation, @command{tar} does nothing.
@ -7874,7 +7858,7 @@ files smaller than 400 blocks in length@footnote{A file system block
is usually 512 bytes, so this amounts to 200K. Use the @samp{c}
suffix to specify size in @emph{bytes}. Also, when using
GNU find, you can specify other size units, such as @samp{k},
@samp{m}, etc. @xref{Size,,,find,GNU Findutils}, for details.} and put that list into a file
@samp{m}, etc. @xref{Size,,,find.info,GNU Findutils}, for details.} and put that list into a file
called @file{small-files}. You can then use the @option{-T} option to
@command{tar} to specify the files from that file, @file{small-files}, to
create the archive @file{little.tgz}. (The @option{-z} option to
@ -8139,7 +8123,7 @@ Contains shell globbing-patterns and regular expressions (if prefixed
with @samp{RE:}@footnote{According to the Bazaar docs,
globbing-patterns are Korn-shell style and regular expressions are
perl-style. As of @GNUTAR{} version @value{VERSION}, these are
treated as shell-style globs and POSIX extended regexps. This will be
treated as shell-style globs and posix extended regexps. This will be
fixed in future releases.}. Patterns affect the directory and all its
subdirectories.
@ -8147,7 +8131,7 @@ Any line beginning with a @samp{#} is a comment.
@findex .hgignore
@item .hgignore
Contains POSIX regular expressions@footnote{Support for perl-style
Contains posix regular expressions@footnote{Support for perl-style
regexps will appear in future releases.}. The line @samp{syntax:
glob} switches to shell globbing patterns. The line @samp{syntax:
regexp} switches back. Comments begin with a @samp{#}. Patterns
@ -8228,19 +8212,9 @@ that match the following shell globbing patterns:
@findex exclude-caches
When creating an archive, the @option{--exclude-caches} option family
causes @command{tar} to exclude all directories that contain a @dfn{cache
directory tag}. A cache directory tag is a regular file with the
well-known name @file{CACHEDIR.TAG} whose first 43 octets
consist of the following ASCII header string:
@example
Signature: 8a477f597d28d172789f06886806bc55
@end example
Any remaining bytes are not relevant, in particular the signature
string does not have to be followed by an LF or CR/LF pair@footnote{The
cache directory tagging specification is set forth in
@url{https://bford.info/cachedir/}}.
directory tag}. A cache directory tag is a short file with the
well-known name @file{CACHEDIR.TAG} and having a standard header
specified in @url{http://www.brynosaurus.com/cachedir/spec.html}.
Various applications write cache directory tags into directories they
use to hold regenerable, non-precious data, so that such data can be
more easily excluded from backups.
@ -8680,7 +8654,7 @@ a\backslash
@end smallexample
Here is how usual @command{ls} command would have listed them, if they
had existed in the working directory:
had existed in the current working directory:
@smallexample
@group
@ -8908,7 +8882,7 @@ extraction.
For example, suppose you have archived whole @file{/usr} hierarchy to
a tar archive named @file{usr.tar}. Among other files, this archive
contains @file{usr/include/stdlib.h}, which you wish to extract to
the working directory. To do so, you type:
the current working directory. To do so, you type:
@smallexample
$ @kbd{tar -xf usr.tar --strip=2 usr/include/stdlib.h}
@ -8916,9 +8890,7 @@ $ @kbd{tar -xf usr.tar --strip=2 usr/include/stdlib.h}
The option @option{--strip=2} instructs @command{tar} to strip the
two leading components (@file{usr/} and @file{include/}) off the file
name, before extracting it. Notice, that archive members to extract are
searched before that modification, hence the file name is specified in
full.
name.
If you add the @option{--verbose} (@option{-v}) option to the invocation
above, you will note that the verbose listing still contains the
@ -8947,7 +8919,7 @@ stdlib.h
@end smallexample
Notice that in both cases the file @file{stdlib.h} is extracted to the
working directory, @option{--show-transformed-names} affects
current working directory, @option{--show-transformed-names} affects
only the way its name is displayed.
This option is especially useful for verifying whether the invocation
@ -9034,6 +9006,7 @@ follows the GNU @command{sed} implementation in this regard, so
the interaction is defined to be: ignore matches before the
@var{number}th, and then match and replace all matches from the
@var{number}th on.
@end table
In addition, several @dfn{transformation scope} flags are supported,
@ -9167,48 +9140,12 @@ order of their appearance. For example, the following two invocations
are equivalent:
@smallexample
$ @kbd{tar -cf arch.tar --transform='s,/usr/var,/var/,' \
$ @kbd{tar -cf arch.tar --transform='s,/usr/var,/var/' \
--transform='s,/usr/local,/usr/,'}
$ @kbd{tar -cf arch.tar \
--transform='s,/usr/var,/var/,;s,/usr/local,/usr/,'}
--transform='s,/usr/var,/var/;s,/usr/local,/usr/,'}
@end smallexample
Applying transformations to some file names may produce empty
strings. This may indicate errors in regular expressions, but it may
as well happen during normal operation, for instance, when using
@option{--strip-components} option with a value which is greater then
or equal to the number of directory components in the topmost
directory stored in the archive. Consider the following example. Let
the archive @file{usr.tar} contain:
@example
@group
$ @kbd{tar tf usr.tar}
usr/
usr/local/
...
@end group
@end example
Extracting from this archive with the @option{--strip=1} option will
transform the name of its first entry (@samp{usr/}) to an empty
string. When this happens, @GNUTAR{} prints a warning and skips this
member:
@example
@group
$ @kbd{tar --strip=1 -xf usr.tar}
tar: usr: transforms to empty name
@end group
@end example
If an empty name results from transformations applied when creating or
updating the archive, the warning is output and the file is not
archived.
If this is intended, you can suppress the warning using the
@option{--warning=no-empty-transform} option (@pxref{warnings}).
@node after
@section Operating Only on New Files
@ -9226,7 +9163,7 @@ to an archive, the archive will only include new files. If you use
@option{--after-date} when extracting an archive, @command{tar} will
only extract files newer than the @var{date} you specify.
If you want @command{tar} to make the date comparison based only on
If you only want @command{tar} to make the date comparison based on
modification of the file's data (rather than status
changes), then use the @option{--newer-mtime=@var{date}} option.
@ -9253,7 +9190,7 @@ name; the data modification time of that file is used as the date.
@opindex newer-mtime
@item --newer-mtime=@var{date}
Act like @option{--after-date}, but look only at data modification times.
Acts like @option{--after-date}, but only looks at data modification times.
@end table
These options limit @command{tar} to operate only on files which have
@ -9272,8 +9209,8 @@ field.
To be precise, @option{--after-date} checks @emph{both} @code{mtime} and
@code{ctime} and processes the file if either one is more recent than
@var{date}, while @option{--newer-mtime} checks only @code{mtime} and
disregards @code{ctime}. Neither option uses @code{atime} (the last time the
@var{date}, while @option{--newer-mtime} only checks @code{mtime} and
disregards @code{ctime}. Neither does it use @code{atime} (the last time the
contents of the file were looked at).
Date specifiers can have embedded spaces. Because of this, you may need
@ -9286,11 +9223,11 @@ $ @kbd{tar -cf foo.tar --newer-mtime '2 days ago'}
@end smallexample
When any of these options is used with the option @option{--verbose}
(@pxref{verbose tutorial}) @GNUTAR{} converts the specified
date back to a textual form and compares that with the
one given with the option. If the two forms differ, @command{tar}
prints both forms in a message, to help the user check that the right
date is being used. For example:
(@pxref{verbose tutorial}) @GNUTAR{} will try to convert the specified
date back to its textual representation and compare that with the
one given with the option. If the two dates differ, @command{tar} will
print a warning saying what date it will use. This is to help user
ensure he is using the right date. For example:
@smallexample
@group
@ -9450,7 +9387,7 @@ $ @kbd{tar -c -f jams.tar grape prune -C food cherry}
@end smallexample
@noindent
will place the files @file{grape} and @file{prune} from the working
will place the files @file{grape} and @file{prune} from the current
directory into the archive @file{jams.tar}, followed by the file
@file{cherry} from the directory @file{food}. This option is especially
useful when you have several widely separated files that you want to
@ -9460,7 +9397,7 @@ Note that the file @file{cherry} is recorded in the archive under the
precise name @file{cherry}, @emph{not} @file{food/cherry}. Thus, the
archive will contain three files that all appear to have come from the
same directory; if the archive is extracted with plain @samp{tar
--extract}, all three files will be written in the working directory.
--extract}, all three files will be written in the current directory.
Contrast this with the command,
@ -9492,8 +9429,8 @@ directories where those files were located.
Note that @option{--directory} options are interpreted consecutively. If
@option{--directory} specifies a relative file name, it is interpreted
relative to the then working directory, which might not be the same as
the original working directory of @command{tar}, due to a previous
relative to the then current directory, which might not be the same as
the original current working directory of @command{tar}, due to a previous
@option{--directory} option.
When using @option{--files-from} (@pxref{files}), you can put various
@ -9532,7 +9469,7 @@ The interpretation of options in file lists is disabled by
@cindex file names, absolute
By default, @GNUTAR{} drops a leading @samp{/} on
input or output, and complains about file names containing a @samp{..}
input or output, and complains about file names containing a @file{..}
component. There is an option that turns off this behavior:
@table @option
@ -9540,8 +9477,7 @@ component. There is an option that turns off this behavior:
@item --absolute-names
@itemx -P
Do not strip leading slashes from file names, and permit file names
containing a @samp{..} file name component, or that escape
the extraction directory.
containing a @file{..} file name component.
@end table
When @command{tar} extracts archive members from an archive, it strips any
@ -9553,10 +9489,9 @@ in the archive. For example, if the archive member has the name
@file{/etc/passwd}, @command{tar} will extract it as if the name were
really @file{etc/passwd}.
File names containing @samp{..} can cause problems when extracting, so
File names containing @file{..} can cause problems when extracting, so
@command{tar} normally warns you about such files when creating an
archive, and prevents attempts to extract such files if that would
affect files outside the working directory.
archive, and rejects attempts to extracts such files.
Other @command{tar} programs do not do this. As a result, if you
create an archive whose member names start with a slash, they will be
@ -9572,17 +9507,52 @@ is not, generally speaking, the same as the one you'd get running
scripts for comparing both outputs. @xref{listing member and file names},
for the information on how to handle this case.}.
Symbolic links containing @file{..} or leading @samp{/} can also cause
problems when extracting, so @command{tar} normally extracts them last;
it may create empty files as placeholders during extraction.
If you use the @option{--absolute-names} (@option{-P}) option,
@command{tar} will do none of these transformations.
To archive or extract files relative to the root directory, specify
the @option{--absolute-names} (@option{-P}) option.
Normally, @command{tar} acts on files relative to the working
directory---ignoring superior directory names when archiving, and
ignoring leading slashes when extracting.
When you specify @option{--absolute-names} (@option{-P}),
@command{tar} stores file names including all superior directory
names, and preserves leading slashes. If you only invoked
@command{tar} from the root directory you would never need the
@option{--absolute-names} option, but using this option
may be more convenient than switching to root.
@FIXME{Should be an example in the tutorial/wizardry section using this
to transfer files between systems.}
By default @command{tar} prints out a message about removing the @samp{/} from
@table @option
@item --absolute-names
Preserves full file names (including superior directory names) when
archiving and extracting files.
@end table
@command{tar} prints out a message about removing the @samp{/} from
file names. This message appears once per @GNUTAR{}
invocation. It represents something which ought to be told; ignoring
what it means can cause very serious surprises, later.
However, to suppress this message change to
Some people, nevertheless, do not want to see this message. Wanting to
play really dangerously, one may of course redirect @command{tar} standard
error to the sink. For example, under @command{sh}:
@smallexample
$ @kbd{tar -c -f archive.tar /home 2> /dev/null}
@end smallexample
@noindent
Another solution, both nicer and simpler, would be to change to
the @file{/} directory first, and then avoid absolute notation.
For example:
@ -9590,15 +9560,8 @@ For example:
$ @kbd{tar -c -f archive.tar -C / home}
@end smallexample
If you use the dangerous options @option{--absolute-names}
(@option{-P}) or @option{--dereference} (@option{-h}),
symbolic links containing @samp{..} or leading @samp{/} can cause
problems when extracting, so @command{tar} extracts them last;
it may create empty files as placeholders during extraction.
Although these placeholders prevent problems if you are extracting
into an empty directory, they do not suffice for nonempty directories.
@xref{Integrity}, for some of the security-related implications
of using these dangerous options.
of using this option.
@include parse-datetime.texi
@ -9633,70 +9596,56 @@ format imposes a number of limitations. The most important of them
are:
@enumerate
@item
File names and symbolic links can contain at most 100 bytes.
@item
File sizes must be less than 8 GiB (@math{2^33} bytes = 8,589,934,592 bytes).
@item
File timestamps must be nonnegative and less than @math{2^33} seconds
since the Epoch, corresponding to the range from 1970-01-01 00:00:00
(inclusive) to 2242-03-16 12:56:32 (exclusive) UTC, ignoring leap seconds.
@item
It is impossible to store special files (block and character
@item The maximum length of a file name is limited to 99 characters.
@item The maximum length of a symbolic link is limited to 99 characters.
@item It is impossible to store special files (block and character
devices, fifos etc.)
@item
UIDs and GIDs must be less than @math{2^21} (2,097,152).
@item
V7 archives do not contain symbolic ownership information (user
@item Maximum value of user or group @acronym{ID} is limited to 2097151 (7777777
octal)
@item V7 archives do not contain symbolic ownership information (user
and group name of the file owner).
@end enumerate
This format has traditionally been used by Automake when producing
Makefiles. This practice will change in the future, in the meantime,
however this means that projects containing file names more than 100
bytes long will not be able to use @GNUTAR{} @value{VERSION} and
however this means that projects containing file names more than 99
characters long will not be able to use @GNUTAR{} @value{VERSION} and
Automake prior to 1.9.
@item ustar
Archive format defined by @acronym{POSIX.1-1988} and later. It stores
Archive format defined by @acronym{POSIX.1-1988} specification. It stores
symbolic ownership information. It is also able to store
special files. However, it imposes several restrictions as well:
@enumerate
@item
File names can contain at most 255 bytes.
@item
File names longer than 100 bytes must be split at a directory separator in
two parts, the first being at most 155 bytes long.
So, in most cases file names must be a bit shorter than 255 bytes.
@item
Symbolic links can contain at most 100 bytes.
@item
File sizes must be less than 8 GiB (@math{2^33} bytes = 8,589,934,592 bytes).
@item
File timestamps must be nonnegative and less than @math{2^33} seconds
since the Epoch, corresponding to the range from 1970-01-01 00:00:00
(inclusive) to 2242-03-16 12:56:32 (exclusive) UTC, ignoring leap seconds.
@item
UIDs, GIDs, device major numbers, and device minor numbers
must be less than @math{2^21} (2,097,152).
@item The maximum length of a file name is limited to 256 characters,
provided that the file name can be split at a directory separator in
two parts, first of them being at most 155 bytes long. So, in most
cases the maximum file name length will be shorter than 256
characters.
@item The maximum length of a symbolic link name is limited to
100 characters.
@item Maximum size of a file the archive is able to accommodate
is 8GB
@item Maximum value of UID/GID is 2097151.
@item Maximum number of bits in device major and minor numbers is 21.
@end enumerate
@item star
The format used by the late J@"org Schilling's @command{star}
implementation. @GNUTAR{} can read @samp{star} archives but
does not produce them.
Format used by J@"org Schilling @command{star}
implementation. @GNUTAR{} is able to read @samp{star} archives but
currently does not produce them.
@item posix
The format defined by @acronym{POSIX.1-2001} and later. This is the
most flexible and feature-rich format. It does not impose arbitrary
restrictions on file sizes or timestamps or file name lengths.
This format is more
recent, so some @command{tar} implementations cannot handle it properly.
However, any @command{tar} implementation able to read @samp{ustar}
archives should be able to read most @samp{posix} archives as well,
except that it will extract any additional information (such as long
file names) as extra plain text files and their parent directories.
Archive format defined by @acronym{POSIX.1-2001} specification. This is the
most flexible and feature-rich format. It does not impose any
restrictions on file sizes or file name lengths. This format is quite
recent, so not all tar implementations are able to handle it properly.
However, this format is designed in such a way that any tar
implementation able to read @samp{ustar} archives will be able to read
most @samp{posix} archives as well, with the only exception that any
additional information (such as long file names etc.)@: will in such
case be extracted as plain text files along with the files it refers to.
This archive format will be the default format for future versions
of @GNUTAR{}.
@ -9706,26 +9655,25 @@ of @GNUTAR{}.
The following table summarizes the limitations of each of these
formats:
@multitable {Format} {no limit} {File Size} {File Date} {File Name} {no limit}
@headitem Format @tab UID @tab File Size @tab File Date @tab File Name @tab Devn
@item gnu @tab 1.8e19 @tab no limit @tab no limit @tab no limit @tab 63
@item oldgnu @tab 1.8e19 @tab no limit @tab no limit @tab no limit @tab 63
@item v7 @tab 2097151 @tab < 8 GiB @tab 1970--2242 @tab 99 @tab n/a
@item ustar @tab 2097151 @tab < 8 GiB @tab 1970--2242 @tab 255 @tab 21
@item posix @tab no limit @tab no limit @tab no limit @tab no limit @tab no limit
@multitable @columnfractions .10 .20 .20 .20 .20
@headitem Format @tab UID @tab File Size @tab File Name @tab Devn
@item gnu @tab 1.8e19 @tab Unlimited @tab Unlimited @tab 63
@item oldgnu @tab 1.8e19 @tab Unlimited @tab Unlimited @tab 63
@item v7 @tab 2097151 @tab 8GB @tab 99 @tab n/a
@item ustar @tab 2097151 @tab 8GB @tab 256 @tab 21
@item posix @tab Unlimited @tab Unlimited @tab Unlimited @tab Unlimited
@end multitable
The default format for @GNUTAR{} is defined at compilation
time. You may check it by running @command{tar --help}, and examining
the last lines of its output. Usually, @GNUTAR{} is configured
to create archives in @samp{gnu} format, however, a future version will
to create archives in @samp{gnu} format, however, future version will
switch to @samp{posix}.
@menu
* Compression:: Using Less Space through Compression
* Attributes:: Handling File Attributes
* Portability:: Making @command{tar} Archives More Portable
* Reproducibility:: Making @command{tar} Archives More Reproducible
* cpio:: Comparison of @command{tar} and @command{cpio}
@end menu
@ -10352,7 +10300,6 @@ This option is meaningless with @option{--list} (@option{-t}).
@node Portability
@section Making @command{tar} Archives More Portable
@cindex portable archives
Creating a @command{tar} archive on a particular system that is meant to be
useful later on many other machines and with other versions of @command{tar}
is more challenging than you might think. @command{tar} archive formats
@ -10411,12 +10358,10 @@ When @option{--dereference} (@option{-h}) is used with
symbolic links point to, instead of
the links themselves.
When creating a portable archive from a directory that adversaries
cannot modify, consider using @option{--dereference}
When creating portable archives, use @option{--dereference}
(@option{-h}): some systems do not support
symbolic links, and moreover, your distribution might be unusable if
it contains unresolved symbolic links.
@xref{dereference}.
When reading from an archive, the @option{--dereference} (@option{-h})
option causes @command{tar} to follow an already-existing symbolic
@ -10426,8 +10371,7 @@ remove the link before writing a new file. @xref{Dealing with Old
Files}.
The @option{--dereference} option is unsafe if an untrusted user can
modify directories while @command{tar} is running, or if extracting
from an untrusted archive into a nonempty directory. @xref{Security}.
modify directories while @command{tar} is running. @xref{Security}.
@node hard links
@subsection Hard Links
@ -10666,8 +10610,8 @@ will use the following default value:
%d/PaxHeaders/%f
@end smallexample
This default helps make the archive more reproducible.
@xref{Reproducibility}. @acronym{POSIX} recommends using
This default is selected to ensure the reproducibility of the
archive. @acronym{POSIX} standard recommends to use
@samp{%d/PaxHeaders.%p/%f} instead, which means the two archives
created with the same set of options and containing the same set
of files will be byte-to-byte different. This default will be used
@ -10768,8 +10712,9 @@ use the following option:
@cindex archives, binary equivalent
@cindex binary equivalent archives, creating
As another example, the following option helps make the archive
more reproducible. @xref{Reproducibility}.
As another example, here is the option that ensures that any two
archives created using it, will be binary equivalent if they have the
same contents:
@smallexample
--pax-option delete=atime
@ -10855,7 +10800,7 @@ file. You will than have to switch to a format that is able to
handle such values. The format summary table (@pxref{Formats}) will
help you to do so.
In particular, when trying to archive files 8 GiB or larger, or with
In particular, when trying to archive files larger than 8GB or with
timestamps not in the range 1970-01-01 00:00:00 through 2242-03-16
12:56:31 @sc{utc}, you will have to chose between @acronym{GNU} and
@acronym{POSIX} archive formats. When considering which format to
@ -10871,9 +10816,7 @@ representations.
On the other hand, @acronym{POSIX} archives, generally speaking, can
be extracted by any tar implementation that understands older
@acronym{ustar} format. The exceptions are files 8 GiB or larger,
or files dated before 1970-01-01 00:00:00 or after 2242-03-16
12:56:31 @sc{utc}
@acronym{ustar} format. The only exception are files larger than 8GB.
@FIXME{Describe how @acronym{POSIX} archives are extracted by non
POSIX-aware tars.}
@ -11047,12 +10990,17 @@ will deduce the name for the resulting expanded file using the
following algorithm:
@enumerate 1
@item
If @file{cond-file} has the form @file{@var{dir}/@var{name}},
the output file name will be @file{@var{dir}/../@var{name}}.
@item If @file{cond-file} does not contain any directories,
@file{../cond-file} will be used;
@item
Otherwise, the output file name will be @file{../cond-file}.
@item If @file{cond-file} has the form
@file{@var{dir}/@var{t}/@var{name}}, where both @var{t} and @var{name}
are simple names, with no @samp{/} characters in them, the output file
name will be @file{@var{dir}/@var{name}}.
@item Otherwise, if @file{cond-file} has the form
@file{@var{dir}/@var{name}}, the output file name will be
@file{@var{name}}.
@end enumerate
In the unlikely case when this algorithm does not suit your needs,
@ -11223,114 +11171,6 @@ Done
@end group
@end smallexample
@node Reproducibility
@section Making @command{tar} Archives More Reproducible
@cindex reproducible archives
Sometimes it is important for an archive to be @dfn{reproducible},
so that one can easily verify it to have been derived solely from
its input. We call an archive reproducible, if an archive
created from the same set of input files with the same command line
options is byte-to-byte equivalent to the original one.
However, two archives created by @GNUTAR{} from two sets of input
files normally might differ even if the input files have the same
contents and @GNUTAR{} was invoked the same way on both sets of input.
This can happen if the inputs have different modification dates or
other metadata, or if the input directories' entries are in different orders.
To avoid this problem when creating an archive, and thus make the
archive reproducible, you can run @GNUTAR{} in the C locale with
some or all of the following options:
@table @option
@item --sort=name
Omit irrelevant information about directory entry order.
@item --format=posix
Avoid problems with large files or files with unusual timestamps.
This also enables @option{--pax-option} options mentioned below.
@item --pax-option='exthdr.name=%d/PaxHeaders/%f'
Omit the process ID of @command{tar}.
This option is needed only if @env{POSIXLY_CORRECT} is set in the environment.
@item --pax-option='delete=atime,delete=ctime'
Omit irrelevant information about file access or status change time.
@item --clamp-mtime --mtime="$SOURCE_EPOCH"
Omit irrelevant information about file timestamps after
@samp{$SOURCE_EPOCH}, which should be a time no less than any
timestamp of any source file.
@item --numeric-owner
Omit irrelevant information about user and group names.
@item --owner=0
@itemx --group=0
Omit irrelevant information about file ownership and group.
@item --mode='go+u,go-w'
Omit irrelevant information about file permissions.
@end table
When creating a reproducible archive from version-controlled source files,
it can be useful to set each file's modification time
to be that of its last commit, so that the timestamps
are reproducible from the version-control repository.
If these timestamps are all on integer second boundaries, and if you use
@option{--format=posix --pax-option='delete=atime,delete=ctime'
--clamp-mtime --mtime="$SOURCE_EPOCH"}
where @code{$SOURCE_EPOCH} is the the time of the most recent commit,
and if all non-source files have timestamps greater than @code{$SOURCE_EPOCH},
then @GNUTAR{} should generate an archive in @acronym{ustar} format,
since no POSIX features will be needed and the archive will be in the
@acronym{ustar} subset of @acronym{posix} format.
Also, if compressing, use a reproducible compression format; e.g.,
with @command{gzip} you should use the @option{--no-name} (@option{-n}) option.
Here is an example set of shell commands to produce a reproducible
tarball with @command{git} and @command{gzip}, which you can tailor to
your project's needs.
@example
function get_commit_time() @{
TZ=UTC0 git log -1 \
--format=tformat:%cd \
--date=format:%Y-%m-%dT%H:%M:%SZ \
"$@@"
@}
#
# Set each source file timestamp to that of its latest commit.
git ls-files | while read -r file; do
commit_time=$(get_commit_time "$file") &&
touch -md $commit_time "$file"
done
#
# Set timestamp of each directory under $FILES
# to the latest timestamp of any descendant.
find $FILES -depth -type d -exec sh -c \
'touch -r "$0/$(ls -At "$0" | head -n 1)" "$0"' \
@{@} ';'
#
# Create $ARCHIVE.tgz from $FILES, pretending that
# the modification time for each newer file
# is that of the most recent commit of any source file.
SOURCE_EPOCH=$(get_commit_time)
TARFLAGS="
--sort=name --format=posix
--pax-option=exthdr.name=%d/PaxHeaders/%f
--pax-option=delete=atime,delete=ctime
--clamp-mtime --mtime=$SOURCE_EPOCH
--numeric-owner --owner=0 --group=0
--mode=go+u,go-w
"
GZIPFLAGS="--no-name --best"
LC_ALL=C tar $TARFLAGS -cf - $FILES |
gzip $GZIPFLAGS > $ARCHIVE.tgz
@end example
@node cpio
@section Comparison of @command{tar} and @command{cpio}
@UNREVISED{}
@ -11665,7 +11505,7 @@ backup tapes.
For example, if the archive contained a file @file{/usr/bin/computoy},
@GNUTAR{} would extract the file to @file{usr/bin/computoy},
relative to the working directory. If you want to extract the files in
relative to the current directory. If you want to extract the files in
an archive to the same absolute names that they had when the archive
was created, you should do a @samp{cd /} before extracting the files
from the archive, or you should either use the @option{--absolute-names}
@ -11793,8 +11633,10 @@ in chunks known as @dfn{records}. To change the default blocking
factor, use the @option{--blocking-factor=@var{512-size}} (@option{-b
@var{512-size}}) option. Each record will then be composed of
@var{512-size} blocks. (Each @command{tar} block is 512 bytes.
@xref{Standard}.) On some devices, a larger record size can be read
and written much more efficiently.
@xref{Standard}.) Each file written to the archive uses at least one
full record. As a result, using a larger record size can result in
more wasted space for small files. On the other hand, a larger record
size can often be read and written much more efficiently.
Further complicating the problem is that some tape drives ignore the
blocking entirely. For these, a larger record size can still improve
@ -13110,35 +12952,28 @@ when you later extract from the archive you will get incorrect data.
When @command{tar} extracts from an archive, by default it writes into
files relative to the working directory. If the archive was generated
by an untrusted user, that user therefore can write into any file
under the working directory. When extracting from an
under the working directory. If the working directory contains a
symbolic link to another directory, the untrusted user can also write
into any file under the referenced directory. When extracting from an
untrusted archive, it is therefore good practice to create an empty
directory and run @command{tar} in that directory. You can use the
@option{--directory} (@option{-C}) option to specify the working
directory (@pxref{directory}).
directory and run @command{tar} in that directory.
When extracting from an archive, @command{tar} by default rejects attempts to
modify files outside the working directory.
For example, if a symbolic link points outside the working directory,
@command{tar} refuses to follow the link, regardless of whether the
symbolic link existed before @command{tar} was run.
Therefore, when extracting from two or more untrusted archives,
each one can be extracted in turn, into the same initially-empty directory.
Even if an earlier archive creates a symbolic link that
points outside the working directory,
@command{tar} will reject any later attempts to follow that symbolic link.
However, this safety mechanism applies only to @command{tar} itself:
it does not apply to other programs you may run later, which will
ordinarily follow symbolic links even if they escape the working directory.
When extracting from two or more untrusted archives, each one should
be extracted independently, into different empty directories.
Otherwise, the first archive could create a symbolic link into an area
outside the working directory, and the second one could follow the
link and overwrite data that is not under the working directory. For
example, when restoring from a series of incremental dumps, the
archives should have been created by a trusted process, as otherwise
the incremental restores might alter data outside the working
directory.
If you use the @option{--absolute-names} (@option{-P}) option when
extracting, @command{tar} respects any file names in the archive, even
file names that begin with @samp{/}, contain @samp{..}, or that follow
a symbolic link to escape the extraction directory.
If you use the @option{--dereference} (@option{-h}) option when extracting,
@command{tar} follows any existing symbolic link that is the last component of
a file name, even if that link escapes the extraction directory.
These two options should be used only for trusted archives, as they
can let an archive overwrite any file in your system that you can owrite.
file names that begin with @file{/} or contain @file{..}. As this
lets the archive overwrite any file in your system that you can write,
the @option{--absolute-names} (@option{-P}) option should be used only
for trusted archives.
Conversely, with the @option{--keep-old-files} (@option{-k}) and
@option{--skip-old-files} options, @command{tar} refuses to replace
@ -13206,7 +13041,7 @@ Protect archives at least as much as you protect any of the files
being archived.
@item
Extract from untrusted archives only into an otherwise-empty
Extract from an untrusted archive only into an otherwise-empty
directory. This directory and its parent should be accessible only to
trusted users. For example:
@ -13219,6 +13054,8 @@ $ @kbd{tar -xvf /archives/got-it-off-the-net.tar.gz}
@end group
@end example
As a corollary, do not do an incremental restore from an untrusted archive.
@item
Do not let untrusted users access files extracted from untrusted
archives without checking first for problems such as setuid programs.

View File

@ -1,4 +1,4 @@
# Copyright 2006-2026 Free Software Foundation, Inc.
# Copyright 2006-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,5 +1,5 @@
@c This is part of GNU tar manual.
@c Copyright 1992--2026 Free Software Foundation, Inc.
@c Copyright 1992--2023 Free Software Foundation, Inc.
@c See file tar.texi for copying conditions.
@macro GNUTAR

2
gnulib

@ -1 +1 @@
Subproject commit 60d20df939832119b9281effea68543510448e2f
Subproject commit 46f9c21a08245fe224fd975de8632b04a0256387

View File

@ -1,7 +1,7 @@
# List of gnulib modules needed for GNU tar.
# A module name per line. Empty lines and comments are ignored.
# Copyright 2005-2026 Free Software Foundation, Inc.
# Copyright 2005-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -18,27 +18,21 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
alloca
areadlinkat-with-size
argmatch
argp
argp-version-etc
assert-h
attribute
backupfile
bool
c-ctype
c32rtomb
c32tolower
c32toupper
closeout
configmake
dirname
dup2
errno-h
error
exclude
exitfail
extern-inline
exitfail
faccessat
fchmodat
fchownat
@ -49,13 +43,12 @@ file-has-acl
fileblocks
flexmember
fnmatch-gnu
fprintftime
free-posix
fseeko
fstatat
full-read
full-write
futimens
gendocs
getline
getopt-gnu
getpagesize
@ -64,66 +57,53 @@ gettime
gitlog-to-changelog
hash
human
ialloc
idx
intprops
inttostr
inttypes-h
issymlinkat
largefile
inttypes
lchown
limits-h
linkat
localcharset
manywarnings
mbrtoc32-regular
mcel-prefer
mkdirat
mkdtemp
mkfifoat
modechange
nstrftime-limited
obstack
openat
openat2
parse-datetime
priv-set
progname
quote
quotearg
readlinkat
reallocarray
renameat
root-uid
rpmatch
safe-read
same-inode
full-read
savedir
selinux-at
setenv
snprintf
stat-time
std-gnu23
stdcountof-h
stddef-h
stdint-h
stdopen
stdbool
stdint
stpcpy
stdopen
strdup-posix
strerror
stringeq
strnlen
strtoimax
strtol
strtoul
strtoumax
symlinkat
sys_stat-h
time_rz
timespec
timespec-sub
unlinkat
unlinkdir
unlocked-io
utimensat
verror
version-etc-fsf
xalignalloc
xalloc
xalloc-die
xgetcwd

1
lib/.gitignore vendored
View File

@ -6,4 +6,5 @@ paxnames.c
rmt-command.h
rmt.h
rtapelib.c
system-ioctl.h
system.h

View File

@ -1,6 +1,6 @@
# Makefile for GNU tar library. -*- Makefile -*-
# Copyright 1994-2026 Free Software Foundation, Inc.
# Copyright 1994-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -33,6 +33,7 @@ noinst_HEADERS = \
paxlib.h\
rmt.h\
system.h\
system-ioctl.h\
wordsplit.h\
xattr-at.h
@ -40,7 +41,7 @@ libtar_a_SOURCES = \
paxerror.c paxexit-status.c paxlib.h paxnames.c \
rtapelib.c \
rmt.h \
system.h \
system.h system-ioctl.h \
wordsplit.c\
xattr-at.c

View File

@ -1,5 +1,5 @@
/* Replacement <attr/xattr.h> for platforms that lack it.
Copyright 2012-2026 Free Software Foundation, Inc.
Copyright 2012-2023 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

File diff suppressed because it is too large Load Diff

View File

@ -18,15 +18,9 @@
#define __WORDSPLIT_H
#include <stddef.h>
#include <idx.h>
#include <attribute.h>
/* This wordsplit code has been tuned for GNU Tar.
Define _WORDSPLIT_EXTRAS before including wordsplit.h
to define extras that GNU Tar does not need. */
#ifdef _WORDSPLIT_EXTRAS
typedef struct wordsplit wordsplit_t;
#endif
/* Structure used to direct the splitting. Members marked with [Input]
can be defined before calling wordsplit(), those marked with [Output]
@ -42,42 +36,42 @@ typedef struct wordsplit wordsplit_t;
default value upon entry to wordsplit(). */
struct wordsplit
{
idx_t ws_wordc; /* [Output] Number of words in ws_wordv. */
size_t ws_wordc; /* [Output] Number of words in ws_wordv. */
char **ws_wordv; /* [Output] Array of parsed out words. */
idx_t ws_offs; /* [Input] (WRDSF_DOOFFS) Number of initial
size_t ws_offs; /* [Input] (WRDSF_DOOFFS) Number of initial
elements in ws_wordv to fill with NULLs. */
idx_t ws_wordn; /* Number of elements ws_wordv can accommodate. */
size_t ws_wordn; /* Number of elements ws_wordv can accommodate. */
unsigned ws_flags; /* [Input] Flags passed to wordsplit. */
unsigned ws_options; /* [Input] (WRDSF_OPTIONS)
Additional options. */
idx_t ws_maxwords; /* [Input] (WRDSO_MAXWORDS) Return at most that
size_t ws_maxwords; /* [Input] (WRDSO_MAXWORDS) Return at most that
many words */
idx_t ws_wordi; /* [Output] (WRDSF_INCREMENTAL) Total number of
size_t ws_wordi; /* [Output] (WRDSF_INCREMENTAL) Total number of
words returned so far */
const char *ws_delim; /* [Input] (WRDSF_DELIM) Word delimiters. */
const char *ws_comment; /* [Input] (WRDSF_COMMENT) Comment characters. */
const char *ws_escape[2]; /* [Input] (WRDSF_ESCAPE) Characters to be escaped
with backslash. */
void (*ws_alloc_die) (struct wordsplit *wsp);
void (*ws_alloc_die) (wordsplit_t *wsp);
/* [Input] (WRDSF_ALLOC_DIE) Function called when
out of memory. Must not return. */
void (*ws_error) (const char *, ...)
_GL_ATTRIBUTE_FORMAT ((printf, 1, 2));
ATTRIBUTE_FORMAT ((printf, 1, 2));
/* [Input] (WRDSF_ERROR) Function used for error
reporting */
void (*ws_debug) (const char *, ...)
_GL_ATTRIBUTE_FORMAT ((printf, 1, 2));
ATTRIBUTE_FORMAT ((printf, 1, 2));
/* [Input] (WRDSF_DEBUG) Function used for debug
output. */
const char **ws_env; /* [Input] (WRDSF_ENV, !WRDSF_NOVAR) Array of
environment variables. */
char **ws_envbuf;
idx_t ws_envidx;
idx_t ws_envsiz;
size_t ws_envidx;
size_t ws_envsiz;
int (*ws_getvar) (char **ret, const char *var, idx_t len, void *clos);
int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
/* [Input] (WRDSF_GETVAR, !WRDSF_NOVAR) Looks up
the name VAR (LEN bytes long) in the table of
variables and if found returns in memory
@ -91,7 +85,7 @@ struct wordsplit
using malloc(3). */
void *ws_closure; /* [Input] (WRDSF_CLOSURE) Passed as the CLOS
argument to ws_getvar and ws_command. */
int (*ws_command) (char **ret, const char *cmd, idx_t len, char **argv,
int (*ws_command) (char **ret, const char *cmd, size_t len, char **argv,
void *clos);
/* [Input] (!WRDSF_NOCMD) Returns in the memory
location pointed to by RET the expansion of
@ -103,19 +97,21 @@ struct wordsplit
return values. */
const char *ws_input; /* Input string (the S argument to wordsplit. */
idx_t ws_len; /* Length of ws_input. */
idx_t ws_endp; /* Points past the last processed byte in
size_t ws_len; /* Length of ws_input. */
size_t ws_endp; /* Points past the last processed byte in
ws_input. */
int ws_errno; /* [Output] Error code, if an error occurred.
This is not the same as a POSIX errno value. */
int ws_errno; /* [Output] Error code, if an error occurred. */
char *ws_usererr; /* Points to textual description of
the error, if ws_errno is WRDSE_USERERR. Must
be allocated with malloc(3). */
struct wordsplit_node *ws_head, *ws_tail;
/* Doubly-linked list of parsed out nodes. */
idx_t ws_lvl; /* Invocation nesting level. */
int ws_lvl; /* Invocation nesting level. */
};
/* Initial size for ws_env, if allocated automatically */
#define WORDSPLIT_ENV_INIT 16
/* Wordsplit flags. */
/* Append the words found to the array resulting from a previous
call. */
@ -228,7 +224,11 @@ struct wordsplit
#define WRDSX_WORD 0
#define WRDSX_QUOTE 1
/* Error codes. */
/* Set escape option F in WS for words (Q==0) or quoted strings (Q==1) */
#define WRDSO_ESC_SET(ws,q,f) ((ws)->ws_options |= ((f) << 4*(q)))
/* Test WS for escape option F for words (Q==0) or quoted strings (Q==1) */
#define WRDSO_ESC_TEST(ws,q,f) ((ws)->ws_options & ((f) << 4*(q)))
#define WRDSE_OK 0
#define WRDSE_EOF WRDSE_OK
#define WRDSE_QUOTE 1
@ -241,26 +241,23 @@ struct wordsplit
#define WRDSE_GLOBERR 8
#define WRDSE_USERERR 9
int wordsplit (char const *s, struct wordsplit *ws, unsigned flags);
void wordsplit_free (struct wordsplit *ws);
char const *wordsplit_strerror (struct wordsplit const *ws);
#ifdef _WORDSPLIT_EXTRAS
int wordsplit_len (const char *s, idx_t len, wordsplit_t *ws, unsigned flags);
int wordsplit (const char *s, wordsplit_t *ws, unsigned flags);
int wordsplit_len (const char *s, size_t len, wordsplit_t *ws, unsigned flags);
void wordsplit_free (wordsplit_t *ws);
void wordsplit_free_words (wordsplit_t *ws);
void wordsplit_free_envbuf (wordsplit_t *ws);
void wordsplit_get_words (wordsplit_t *ws, idx_t *wordc, char ***wordv);
int wordsplit_get_words (wordsplit_t *ws, size_t *wordc, char ***wordv);
int wordsplit_append (wordsplit_t *wsp, int argc, char **argv);
char wordsplit_c_unquote_char (char c);
char wordsplit_c_quote_char (char c);
idx_t wordsplit_c_quoted_length (const char *str, bool quote_hex, bool *quote);
void wordsplit_c_quote_copy (char *dst, const char *src, bool quote_hex);
int wordsplit_c_unquote_char (int c);
int wordsplit_c_quote_char (int c);
size_t wordsplit_c_quoted_length (const char *str, int quote_hex, int *quote);
void wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex);
void wordsplit_perror (wordsplit_t *ws);
const char *wordsplit_strerror (wordsplit_t *ws);
void wordsplit_clearerr (wordsplit_t *ws);
#endif
#endif

View File

@ -1,7 +1,7 @@
/* openat-style fd-relative functions for operating with extended file
attributes.
Copyright 2012-2026 Free Software Foundation, Inc.
Copyright 2012-2023 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

View File

@ -1,7 +1,7 @@
/* Prototypes for openat-style fd-relative functions for operating with
extended file attributes.
Copyright 2012-2026 Free Software Foundation, Inc.
Copyright 2012-2023 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
@ -51,7 +51,7 @@ int lsetxattrat (int dir_fd, const char *path, const char *name,
const void *value, size_t size, int flags);
/* dir-fd-relative getxattr. Operation gets the VALUE of the extended
attribute identified by NAME and associated with the given PATH in the
attribute idenfified by NAME and associated with the given PATH in the
filesystem relatively to directory identified by DIR_FD. For more info
about all parameters see the getxattr(2) manpage. */
ssize_t getxattrat (int dir_fd, const char *path, const char *name,

@ -1 +1 @@
Subproject commit bb78da089e1086c9403a29d838231f50e9ff25c4
Subproject commit 481bae11050fcbdca67a66eb57390267b280a312

View File

@ -1,6 +1,6 @@
# List of files which contain translatable strings.
# Copyright 1996-2026 Free Software Foundation, Inc.
# Copyright 1996-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,6 +1,6 @@
# Make GNU tar scripts.
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -82,7 +82,7 @@ SLEEP_MESSAGE="`awk '
}' /dev/null`"
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -158,7 +158,7 @@ message 20 "BACKUP_FILES=$BACKUP_FILES"
if ! $MT_BEGIN "${TAPE_FILE}"; then
echo >&2 "$0: tape initialization failed"
exit 1
fi
fi
rm -f "${VOLNO_FILE}"
message 1 "processing backup directories"

View File

@ -1,7 +1,7 @@
#! /bin/sh
# Make backups.
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -342,7 +342,7 @@ remote_run() {
license() {
cat - <<EOF
Copyright (C) 2013, 2025-2026 Free Software Foundation, Inc.
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

View File

@ -8,7 +8,7 @@
# interested parties that a tape for the next volume of the backup needs to
# be put in the tape drive.
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
#! /bin/sh
# Restore backups.
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,6 +1,6 @@
#! /usr/bin/perl -w
# Display and edit the 'dev' field in tar's snapshots
# Copyright 2007-2026 Free Software Foundation, Inc.
# Copyright 2007-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -55,7 +55,7 @@
# * when printing the list of device ids found in the snapshot file
# (when run in the default mode), print the raw device id values
# instead of the hex-string version in those cases where they
# can't be converted successfully.
# can't be converted successfully.
use Getopt::Std;
use Config;
@ -236,13 +236,13 @@ sub show_device_counts ($) {
# negative. (If it's negative, the conversion to an unsigned
# integer for the "%x" specifier will mean the result will
# always trigger hex()'s warning on a 64-bit machine.)
#
#
# These situations don't seem to occur very often, so for now
# when they do occur, we simply print the original text value
# that was read from the snapshot file; it will look a bit
# funny next to the values that do print in hex, but that's
# preferable to printing values that aren't actually correct.
$devstr = $dev;
$devstr = $dev;
}
printf " Device %s occurs $devices{$dev} times.\n", $devstr;
}
@ -263,7 +263,7 @@ sub show_device_counts ($) {
# array defined below should match those calculations. (For tar v1.27
# and later, the valid ranges for a particular tar binary can easily
# be determined using the "tar --show-snapshot-field-ranges" command.)
sub choose_architecture ($) {
my $opt_a = shift;
@ -307,7 +307,7 @@ sub choose_architecture ($) {
print "Unrecognized architecture \"$arch\"; defaulting to \"iX86-linux\".\n";
print "(Use -a option to override.)\n" unless $opt_a;
print "\n";
}
}
if ( ref(1) ne "" ) {
print "(\"bignum\" mode is in effect; skipping 64-bit-integer check.)\n\n"
@ -317,10 +317,10 @@ sub choose_architecture ($) {
for $v (values %snapshot_field_ranges ) {
$maxmax = $v->[1] if ($v->[1] > $maxmax);
}
# "~0" translates into a platform-native integer with all bits turned
# on -- that is, the largest value that can be represented as
# an integer. We print a warning if our $maxmax value is greater
# an integer. We print a warning if our $maxmax value is greater
# than that largest integer, since in that case Perl will switch
# to using floats for those large max values. The wording of
# the message assumes that the only way this situation can exist
@ -342,11 +342,11 @@ Note: this version of Perl uses 32-bit integers, which means that it
EOF
}
}
}
# returns a warning message if $field_value isn't a valid string
# returns a warning message if $field_value isn't a valid string
# representation of an integer, or if the resulting integer is out of range
# defined by the two-element array retrieved using up the $field_name key in
# the global %snapshot_field_ranges hash.
@ -382,8 +382,8 @@ sub validate_integer_field ($$) {
# other two were introduced in v1.27.)
#
# The checks here are intended to match those found in the incremen.c
# source file. See the choose_architecture() function (above) for more
# information on how to configure the range of values considered valid
# source file. See the choose_architecture() function (above) for more
# information on how to configure the range of values considered valid
# by this script.
#
# (Note: the checks here are taken from the code that processes
@ -602,16 +602,16 @@ Usage:
that would cause \"tar\" to abort with an error message such as
Unexpected field value in snapshot file
Numerical result out of range
or
or
Invalid argument
as it processed the snapshot file.)
Normally the program automatically chooses the valid ranges for
the fields based on the current system's architecture, but the
-a option can be used to override the selection, e.g. in order
Normally the program automatically chooses the valid ranges for
the fields based on the current system's architecture, but the
-a option can be used to override the selection, e.g. in order
to validate a snapshot file generated on a some other system.
(Currently only three architectures are supported, "iX86-linux",
"x86_64-linux", and "IA64.ARCHREV_0" [HP/UX running on Itanium/ia64],
"x86_64-linux", and "IA64.ARCHREV_0" [HP/UX running on Itanium/ia64],
and if the current system isn't recognized, then the iX86-linux
values are used by default.)

View File

@ -3,7 +3,7 @@
# concatenates a GNU tar multi-volume archive into a single tar archive.
# Author: Bruno Haible <bruno@clisp.org>, Sergey Poznyakoff <gray@gnu.org.ua>
# Copyright 2004-2026 Free Software Foundation, Inc.
# Copyright 2004-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
/* xsparse - expands compressed sparse file images extracted from GNU tar
archives.
Copyright 2006-2026 Free Software Foundation, Inc.
Copyright 2006-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -20,17 +20,16 @@
Written by Sergey Poznyakoff */
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
/* Bound on length of the string representing an off_t.
See INT_STRLEN_BOUND in intprops.h for explanation */
@ -45,10 +44,10 @@ struct sp_array
off_t numbytes;
};
static char *progname;
static bool verbose;
char *progname;
int verbose;
static void
void
die (int code, char *fmt, ...)
{
va_list ap;
@ -61,42 +60,58 @@ die (int code, char *fmt, ...)
exit (code);
}
static void *
void *
emalloc (size_t size)
{
char *p = malloc (size);
if (!p && size)
if (!p)
die (1, "not enough memory");
return p;
}
static off_t
off_t
string_to_off (char *p, char **endp)
{
errno = 0;
intmax_t i = strtoimax (p, endp, 10);
off_t v = i;
if (i < 0 || v != i || errno == ERANGE)
die (1, "number out of allowed range, near %s", p);
if (errno || p == *endp)
die (1, "number parse error near %s", p);
off_t v = 0;
for (; *p; p++)
{
int digit = *p - '0';
off_t x = v * 10;
if (9 < (unsigned) digit)
{
if (endp)
{
*endp = p;
break;
}
die (1, "number parse error near %s", p);
}
else if (x / 10 != v)
die (1, "number out of allowed range, near %s", p);
v = x + digit;
if (v < 0)
die (1, "negative number");
}
if (endp)
*endp = p;
return v;
}
static size_t
string_to_size (char *p, char **endp, size_t maxsize)
size_t
string_to_size (char *p, char **endp)
{
off_t v = string_to_off (p, endp);
size_t ret = v;
if (! (ret == v && ret <= maxsize))
if (ret != v)
die (1, "number too big");
return ret;
}
static size_t sparse_map_size;
static struct sp_array *sparse_map;
size_t sparse_map_size;
struct sp_array *sparse_map;
static void
void
get_line (char *s, int size, FILE *stream)
{
char *p = fgets (s, size, stream);
@ -106,11 +121,11 @@ get_line (char *s, int size, FILE *stream)
die (1, "unexpected end of file");
len = strlen (p);
if (s[len - 1] != '\n')
die (1, "invalid or too-long data");
s[len - 1] = '\0';
die (1, "buffer overflow");
s[len - 1] = 0;
}
static bool
int
get_var (FILE *fp, char **name, char **value)
{
static char *buffer;
@ -123,12 +138,12 @@ get_var (FILE *fp, char **name, char **value)
size_t len, s;
if (!fgets (buffer, bufsize, fp))
return false;
return 0;
len = strlen (buffer);
if (len == 0)
return false;
return 0;
s = string_to_size (buffer, &p, SIZE_MAX - 1);
s = string_to_size (buffer, &p);
if (*p != ' ')
die (1, "malformed header: expected space but found %s", p);
if (buffer[len-1] != '\n')
@ -145,7 +160,7 @@ get_var (FILE *fp, char **name, char **value)
}
p++;
}
while (strncmp (p, "GNU.sparse.", 11) != 0);
while (memcmp (p, "GNU.sparse.", 11));
p += 11;
q = strchr (p, '=');
@ -155,19 +170,20 @@ get_var (FILE *fp, char **name, char **value)
q[strlen (q) - 1] = 0;
*name = p;
*value = q;
return true;
return 1;
}
static char *outname;
static off_t outsize;
static off_t version_major;
static off_t version_minor;
char *outname;
off_t outsize;
unsigned version_major;
unsigned version_minor;
static void
void
read_xheader (char *name)
{
char *kw, *val;
FILE *fp = fopen (name, "r");
char *expect = NULL;
size_t i = 0;
if (verbose)
@ -178,6 +194,10 @@ read_xheader (char *name)
if (verbose)
printf ("Found variable GNU.sparse.%s = %s\n", kw, val);
if (expect && strcmp (kw, expect))
die (1, "bad keyword sequence: expected '%s' but found '%s'",
expect, kw);
expect = NULL;
if (strcmp (kw, "name") == 0)
{
outname = emalloc (strlen (val) + 1);
@ -185,11 +205,11 @@ read_xheader (char *name)
}
else if (strcmp (kw, "major") == 0)
{
version_major = string_to_off (val, NULL);
version_major = string_to_size (val, NULL);
}
else if (strcmp (kw, "minor") == 0)
{
version_minor = string_to_off (val, NULL);
version_minor = string_to_size (val, NULL);
}
else if (strcmp (kw, "realsize") == 0
|| strcmp (kw, "size") == 0)
@ -198,27 +218,17 @@ read_xheader (char *name)
}
else if (strcmp (kw, "numblocks") == 0)
{
sparse_map_size = string_to_size (val, NULL,
SIZE_MAX / sizeof *sparse_map);
if (sparse_map_size)
{
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
sparse_map[0].offset = -1;
}
sparse_map_size = string_to_size (val, NULL);
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
}
else if (strcmp (kw, "offset") == 0)
{
if (sparse_map_size <= i)
die (1, "bad GNU.sparse.map: spurious offset");
sparse_map[i].offset = string_to_off (val, NULL);
expect = "numbytes";
}
else if (strcmp (kw, "numbytes") == 0)
{
if (sparse_map_size <= i || sparse_map[i].offset < 0)
die (1, "bad GNU.sparse.map: spurious numbytes");
sparse_map[i++].numbytes = string_to_off (val, NULL);
if (i < sparse_map_size)
sparse_map[i].offset = -1;
}
else if (strcmp (kw, "map") == 0)
{
@ -242,15 +252,16 @@ read_xheader (char *name)
die (1, "bad GNU.sparse.map: garbage at the end");
}
}
if (expect)
die (1, "bad keyword sequence: expected '%s' not found", expect);
if (version_major == 0 && sparse_map_size == 0)
die (1, "size of the sparse map unknown");
if (i != sparse_map_size)
die (1, "not all sparse entries supplied");
if (ferror (fp) || fclose (fp) < 0)
die (1, "read error: %s", name);
fclose (fp);
}
static void
void
read_map (FILE *ifp)
{
size_t i;
@ -260,7 +271,7 @@ read_map (FILE *ifp)
printf ("Reading v.1.0 sparse map\n");
get_line (nbuf, sizeof nbuf, ifp);
sparse_map_size = string_to_size (nbuf, NULL, SIZE_MAX / sizeof *sparse_map);
sparse_map_size = string_to_size (nbuf, NULL);
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
for (i = 0; i < sparse_map_size; i++)
@ -271,51 +282,52 @@ read_map (FILE *ifp)
sparse_map[i].numbytes = string_to_off (nbuf, NULL);
}
off_t ifp_offset = ftello (ifp);
if (ifp_offset < 0)
die (1, "ftello");
if (ifp_offset % BLOCKSIZE != 0
&& fseeko (ifp, BLOCKSIZE - ifp_offset % BLOCKSIZE, SEEK_CUR) < 0)
die (1, "fseeko");
fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
SEEK_SET);
}
static void
void
expand_sparse (FILE *sfp, int ofd)
{
size_t i;
off_t max_numbytes = 0;
size_t maxbytes;
char *buffer;
for (i = 0; i < sparse_map_size; i++)
if (max_numbytes < sparse_map[i].numbytes)
max_numbytes = sparse_map[i].numbytes;
maxbytes = max_numbytes < SIZE_MAX ? max_numbytes : SIZE_MAX;
for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
if (maxbytes == 0)
die (1, "not enough memory");
for (i = 0; i < sparse_map_size; i++)
{
off_t size = sparse_map[i].numbytes;
if (size == 0)
{
if (0 <= ofd && ftruncate (ofd, sparse_map[i].offset) < 0)
die (1, "ftruncate error (%d)", errno);
}
ftruncate (ofd, sparse_map[i].offset);
else
{
if (0 <= ofd && lseek (ofd, sparse_map[i].offset, SEEK_SET) < 0)
die (1, "lseek error (%d)", errno);
lseek (ofd, sparse_map[i].offset, SEEK_SET);
while (size)
{
char buffer[BUFSIZ];
size_t rdsize = size < BUFSIZ ? size : BUFSIZ;
size_t rdsize = (size < maxbytes) ? size : maxbytes;
if (rdsize != fread (buffer, 1, rdsize, sfp))
die (1, "read error (%d)", errno);
if (0 <= ofd)
{
ssize_t written = write (ofd, buffer, rdsize);
if (written != rdsize)
die (1, "write error (%d)", written < 0 ? errno : 0);
}
if (rdsize != write (ofd, buffer, rdsize))
die (1, "write error (%d)", errno);
size -= rdsize;
}
}
}
free (buffer);
}
static void
void
usage (int code)
{
printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
@ -330,26 +342,58 @@ usage (int code)
exit (code);
}
static void
void
guess_outname (char *name)
{
char *base = strrchr (name, '/');
base = base ? base + 1 : name;
size_t dirlen = base - name, baselen = strlen (base);
static char const parentdir[] = "../";
int parentdirlen = sizeof parentdir - 1;
outname = emalloc (dirlen + parentdirlen + baselen + 1);
memcpy (outname, name, dirlen);
memcpy (outname + dirlen, parentdir, parentdirlen);
memcpy (outname + dirlen + parentdirlen, base, baselen + 1);
char *p;
char *s;
if (name[0] == '.' && name[1] == '/')
name += 2;
p = name + strlen (name) - 1;
s = NULL;
for (; p > name && *p != '/'; p--)
;
if (*p == '/')
s = p + 1;
if (p != name)
{
for (p--; p > name && *p != '/'; p--)
;
}
if (*p != '/')
{
if (s)
outname = s;
else
{
outname = emalloc (4 + strlen (name));
strcpy (outname, "../");
strcpy (outname + 3, name);
}
}
else
{
size_t len = p - name + 1;
outname = emalloc (len + strlen (s) + 1);
memcpy (outname, name, len);
strcpy (outname + len, s);
}
}
int
main (int argc, char **argv)
{
int c;
bool dry_run = false;
int dry_run = 0;
char *xheader_file = NULL;
char *inname;
FILE *ifp;
struct stat st;
int ofd;
progname = argv[0];
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
@ -365,9 +409,9 @@ main (int argc, char **argv)
break;
case 'n':
dry_run = true;
dry_run = 1;
case 'v':
verbose = true;
verbose++;
break;
default:
@ -384,16 +428,15 @@ main (int argc, char **argv)
if (xheader_file)
read_xheader (xheader_file);
char *inname = argv[0];
inname = argv[0];
if (argv[1])
outname = argv[1];
struct stat st;
if (stat (inname, &st) < 0)
if (stat (inname, &st))
die (1, "cannot stat %s (%d)", inname, errno);
FILE *ifp = fopen (inname, "r");
if (!ifp)
ifp = fopen (inname, "r");
if (ifp == NULL)
die (1, "cannot open file %s (%d)", inname, errno);
if (!xheader_file || version_major == 1)
@ -402,36 +445,30 @@ main (int argc, char **argv)
if (!outname)
guess_outname (inname);
ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
if (ofd == -1)
die (1, "cannot open file %s (%d)", outname, errno);
if (verbose)
printf ("Expanding file '%s' to '%s'\n", inname, outname);
int ofd = -1;
if (!dry_run)
{
ofd = open (outname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode);
if (ofd < 0)
die (1, "cannot open file %s (%d)", outname, errno);
}
expand_sparse (ifp, ofd);
if (ferror (ifp) || fclose (ifp) < 0)
die (1, "input error: %s", inname);
if (close (ofd) < 0)
die (1, "output error: %s", outname);
if (verbose)
printf ("Done\n");
if (dry_run)
{
printf ("Finished dry run\n");
return 0;
}
expand_sparse (ifp, ofd);
fclose (ifp);
close (ofd);
if (verbose)
printf ("Done\n");
if (outsize)
{
if (stat (outname, &st) < 0)
if (stat (outname, &st))
die (1, "cannot stat output file %s (%d)", outname, errno);
if (st.st_size != outsize)
die (1, "expanded file has wrong size");

View File

@ -1,6 +1,6 @@
# Makefile for GNU tar sources.
# Copyright 1994-2026 Free Software Foundation, Inc.
# Copyright 1994-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -50,7 +50,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
tar_LDADD = $(LIBS) ../lib/libtar.a ../gnu/libgnu.a\
$(LIB_ACL) $(QCOPY_ACL_LIB) $(CLOCK_TIME_LIB) $(EUIDACCESS_LIBGEN)\
$(GETRANDOM_LIB) $(HARD_LOCALE_LIB) $(FILE_HAS_ACL_LIB) $(MBRTOWC_LIB)\
$(LIB_SELINUX) $(SETLOCALE_NULL_LIB) \
$(LIBINTL) $(LIBICONV)
$(LIB_ACL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)\
$(LIB_GETRANDOM) $(LIB_HARD_LOCALE) $(FILE_HAS_ACL_LIB) $(LIB_MBRTOWC)\
$(LIB_SELINUX) $(LIB_SETLOCALE_NULL)

View File

@ -1,5 +1,5 @@
/* Long integers, for GNU tar.
Copyright 1999-2026 Free Software Foundation, Inc.
Copyright 1999-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* Checkpoint management for tar.
Copyright 2007-2026 Free Software Foundation, Inc.
Copyright 2007-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -19,13 +19,10 @@
#include <system.h>
#include "common.h"
#include <wordsplit.h>
#include <flexmember.h>
#include <strftime.h>
#include "wordsplit.h"
#include <sys/ioctl.h>
#include <termios.h>
#include "fprintftime.h"
#include <signal.h>
enum checkpoint_opcode
@ -50,83 +47,103 @@ struct checkpoint_action
char *command;
int signal;
} v;
char commandbuf[FLEXIBLE_ARRAY_MEMBER];
};
/* Checkpointing counter */
static intmax_t checkpoint;
static unsigned checkpoint;
/* List of checkpoint actions */
static struct checkpoint_action *checkpoint_action,
**checkpoint_action_tail = &checkpoint_action;
static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
/* State of the checkpoint system */
static enum {
enum {
CHKP_INIT, /* Needs initialization */
CHKP_COMPILE, /* Actions are being compiled */
CHKP_RUN /* Actions are being run */
} checkpoint_state;
};
static int checkpoint_state;
/* Blocked signals */
static sigset_t sigs;
static struct checkpoint_action *
alloc_action (enum checkpoint_opcode opcode, char const *quoted_string)
alloc_action (enum checkpoint_opcode opcode)
{
idx_t quoted_size = quoted_string ? strlen (quoted_string) + 1 : 0;
struct checkpoint_action *p = xmalloc (FLEXSIZEOF (struct checkpoint_action,
commandbuf, quoted_size));
*checkpoint_action_tail = p;
checkpoint_action_tail = &p->next;
p->next = NULL;
struct checkpoint_action *p = xzalloc (sizeof *p);
if (checkpoint_action_tail)
checkpoint_action_tail->next = p;
else
checkpoint_action = p;
checkpoint_action_tail = p;
p->opcode = opcode;
if (quoted_string)
{
p->v.command = memcpy (p->commandbuf, quoted_string, quoted_size);
unquote_string (p->v.command);
}
return p;
}
static char *
copy_string_unquote (const char *str)
{
char *output = xstrdup (str);
size_t len = strlen (output);
if ((*output == '"' || *output == '\'')
&& len > 1 && output[len-1] == *output)
{
memmove (output, output+1, len-2);
output[len-2] = 0;
}
unquote_string (output);
return output;
}
void
checkpoint_compile_action (const char *str)
{
struct checkpoint_action *act;
if (checkpoint_state == CHKP_INIT)
{
sigemptyset (&sigs);
checkpoint_state = CHKP_COMPILE;
}
if (streq (str, ".") || streq (str, "dot"))
alloc_action (cop_dot, NULL);
else if (streq (str, "bell"))
alloc_action (cop_bell, NULL);
else if (streq (str, "echo"))
alloc_action (cop_echo, NULL)->v.command = NULL;
if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
alloc_action (cop_dot);
else if (strcmp (str, "bell") == 0)
alloc_action (cop_bell);
else if (strcmp (str, "echo") == 0)
alloc_action (cop_echo);
else if (strncmp (str, "echo=", 5) == 0)
alloc_action (cop_echo, str + 5);
{
act = alloc_action (cop_echo);
act->v.command = copy_string_unquote (str + 5);
}
else if (strncmp (str, "exec=", 5) == 0)
alloc_action (cop_exec, str + 5);
{
act = alloc_action (cop_exec);
act->v.command = copy_string_unquote (str + 5);
}
else if (strncmp (str, "ttyout=", 7) == 0)
alloc_action (cop_ttyout, str + 7);
{
act = alloc_action (cop_ttyout);
act->v.command = copy_string_unquote (str + 7);
}
else if (strncmp (str, "sleep=", 6) == 0)
{
char const *arg = str + 6;
char *p;
alloc_action (cop_sleep, NULL)->v.time
= stoint (arg, &p, NULL, 0, TYPE_MAXIMUM (time_t));
if ((p == arg) | *p)
paxfatal (0, _("%s: not a valid timeout"), str);
time_t n = strtoul (str+6, &p, 10);
if (*p)
FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
act = alloc_action (cop_sleep);
act->v.time = n;
}
else if (streq (str, "totals"))
alloc_action (cop_totals, NULL);
else if (strcmp (str, "totals") == 0)
alloc_action (cop_totals);
else if (strncmp (str, "wait=", 5) == 0)
{
int sig = decode_signal (str + 5);
alloc_action (cop_wait, NULL)->v.signal = sig;
sigaddset (&sigs, sig);
act = alloc_action (cop_wait);
act->v.signal = decode_signal (str + 5);
sigaddset (&sigs, act->v.signal);
}
else
paxfatal (0, _("%s: unknown checkpoint action"), str);
FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
}
void
@ -152,10 +169,13 @@ checkpoint_finish_compile (void)
}
}
/* Get the number of columns in the FP output stream.
FIXME: The rest of the code counts bytes, not columns,
so columns don't line up if multi-byte characters are output. */
static intmax_t
static const char *checkpoint_total_format[] = {
"R",
"W",
"D"
};
static long
getwidth (FILE *fp)
{
char const *columns;
@ -169,9 +189,8 @@ getwidth (FILE *fp)
columns = getenv ("COLUMNS");
if (columns)
{
char *end;
intmax_t col = stoint (columns, &end, NULL, 0, INTMAX_MAX);
if (! (*end | !col))
long int col = strtol (columns, NULL, 10);
if (0 < col)
return col;
}
@ -179,20 +198,24 @@ getwidth (FILE *fp)
}
static char *
getarg (char const *input, char const **endp, char **argbuf, idx_t *arglen)
getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
{
if (input[0] == '{')
{
char const *p = strchr (input + 1, '}');
char *p = strchr (input + 1, '}');
if (p)
{
idx_t n = p - input;
size_t n = p - input;
if (n > *arglen)
*argbuf = xpalloc (*argbuf, arglen, n - *arglen, -1, 1);
{
*arglen = n;
*argbuf = xrealloc (*argbuf, *arglen);
}
n--;
*endp = p + 1;
memcpy (*argbuf, input + 1, n);
(*argbuf)[n] = 0;
return memcpy (*argbuf, input + 1, n);
*endp = p + 1;
return *argbuf;
}
}
@ -200,32 +223,38 @@ getarg (char const *input, char const **endp, char **argbuf, idx_t *arglen)
return NULL;
}
static bool tty_cleanup;
static int tty_cleanup;
static const char *def_format =
"%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
static intmax_t
format_checkpoint_string (FILE *fp, intmax_t len,
static int
format_checkpoint_string (FILE *fp, size_t len,
const char *input, bool do_write,
intmax_t cpn)
unsigned cpn)
{
const char *opstr = do_write ? gettext ("write") : gettext ("read");
char uintbuf[UINTMAX_STRSIZE_BOUND];
char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
const char *ip;
static char *argbuf = NULL;
static idx_t arglen = 0;
static size_t arglen = 0;
char *arg = NULL;
if (!input)
{
if (do_write)
/* TRANSLATORS: This is a "checkpoint of write operation",
*not* "Writing a checkpoint". */
*not* "Writing a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de escritura",
*not* "Escribiendo un punto de comprobaci@'on" */
input = gettext ("Write checkpoint %u");
else
/* TRANSLATORS: This is a "checkpoint of read operation",
*not* "Reading a checkpoint". */
*not* "Reading a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de lectura",
*not* "Leyendo un punto de comprobaci@'on" */
input = gettext ("Read checkpoint %u");
}
@ -240,52 +269,44 @@ format_checkpoint_string (FILE *fp, intmax_t len,
{
fputc ('%', fp);
fputc (*ip, fp);
len = add_printf (len, 2);
len += 2;
continue;
}
}
switch (*ip)
{
case 'c':
len = add_printf (len,
format_checkpoint_string (fp, len, def_format,
do_write, cpn));
len += format_checkpoint_string (fp, len, def_format, do_write,
cpn);
break;
case 'u':
len = add_printf (len, fprintf (fp, "%jd", cpn));
fputs (cps, fp);
len += strlen (cps);
break;
case 's':
fputs (opstr, fp);
len = add_printf (len, strlen (opstr));
len += strlen (opstr);
break;
case 'd':
len = add_printf (len,
fprintf (fp, "%.0f",
compute_duration_ns () / BILLION));
len += fprintf (fp, "%.0f", compute_duration ());
break;
case 'T':
{
static char const *const checkpoint_total_format[]
= { "R", "W", "D" };
char const *const *fmt = checkpoint_total_format, *fmtbuf[3];
const char **fmt = checkpoint_total_format, *fmtbuf[3];
struct wordsplit ws;
compute_duration_ns ();
compute_duration ();
if (arg)
{
ws.ws_delim = ",";
if (wordsplit (arg, &ws,
(WRDSF_NOVAR | WRDSF_NOCMD
| WRDSF_QUOTE | WRDSF_DELIM))
!= WRDSE_OK)
paxerror (0, _("cannot split string '%s': %s"),
arg, wordsplit_strerror (&ws));
else if (3 < ws.ws_wordc)
paxerror (0, _("too many words in '%s'"), arg);
if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
WRDSF_QUOTE | WRDSF_DELIM))
ERROR ((0, 0, _("cannot split string '%s': %s"),
arg, wordsplit_strerror (&ws)));
else
{
int i;
@ -297,7 +318,7 @@ format_checkpoint_string (FILE *fp, intmax_t len,
fmt = fmtbuf;
}
}
len = add_printf (len, format_total_stats (fp, fmt, ',', 0));
len += format_total_stats (fp, fmt, ',', 0);
if (arg)
wordsplit_free (&ws);
}
@ -305,68 +326,28 @@ format_checkpoint_string (FILE *fp, intmax_t len,
case 't':
{
struct timespec ts = current_timespec ();
struct tm *tm = localtime (&ts.tv_sec);
struct timeval tv;
struct tm *tm;
const char *fmt = arg ? arg : "%c";
#if HAVE_STRUCT_TM_TM_GMTOFF && HAVE_STRUCT_TM_TM_ZONE
/* struct tm has POSIX.1-2024 tm_gmtoff and tm_zone,
so nstrftime ignores tz and any tz value will do. */
timezone_t tz = NULL;
#else
static timezone_t tz;
if (tm && !tz)
{
tz = tzalloc (getenv ("TZ"));
if (!tz)
tm = NULL;
}
#endif
/* Keep BUF relatively small, as any text timestamp
not fitting into BUF is likely a DoS attack. */
char buf[max (TIMESPEC_STRSIZE_BOUND, 256)];
ptrdiff_t buflen =
(tm
? nstrftime (buf, sizeof buf, arg ? arg : "%c",
tm, tz, ts.tv_nsec)
: -1);
char const *tmstr;
idx_t tmstrlen;
if (buflen < 0)
{
tmstr = code_timespec (ts, buf);
tmstrlen = strlen (tmstr);
}
else
{
tmstr = buf;
tmstrlen = buflen;
}
len = add_printf (len, fwrite (tmstr, 1, tmstrlen, stdout));
gettimeofday (&tv, NULL);
tm = localtime (&tv.tv_sec);
len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
}
break;
case '*':
if (0 <= len)
{
intmax_t w;
if (!arg)
w = getwidth (fp);
else
{
char *end;
w = stoint (arg, &end, NULL, 0, INTMAX_MAX);
if ((end == arg) | *end)
w = 80;
}
for (; w > len; len++)
fputc (' ', fp);
}
{
long w = arg ? strtol (arg, NULL, 10) : getwidth (fp);
for (; w > len; len++)
fputc (' ', fp);
}
break;
default:
fputc ('%', fp);
fputc (*ip, fp);
len = add_printf (len, 2);
len += 2;
break;
}
arg = NULL;
@ -377,10 +358,10 @@ format_checkpoint_string (FILE *fp, intmax_t len,
if (*ip == '\r')
{
len = 0;
tty_cleanup = true;
tty_cleanup = 1;
}
else
len = add_printf (len, 1);
len++;
}
}
fflush (fp);
@ -441,7 +422,7 @@ run_checkpoint_actions (bool do_write)
break;
case cop_totals:
compute_duration_ns ();
compute_duration ();
print_total_stats ();
break;
@ -466,7 +447,7 @@ checkpoint_flush_actions (void)
case cop_ttyout:
if (tty && tty_cleanup)
{
intmax_t w = getwidth (tty);
long w = getwidth (tty);
while (w--)
fputc (' ', tty);
fputc ('\r', tty);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* Diff files from a tar archive.
Copyright 1988-2026 Free Software Foundation, Inc.
Copyright 1988-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -20,21 +20,15 @@
Written by John Gilmore, on 1987-04-30. */
#include <system.h>
#include <system-ioctl.h>
#if HAVE_LINUX_FD_H
# include <linux/fd.h>
#endif
#if HAVE_SYS_MTIO_H
# include <sys/ioctl.h>
# include <sys/mtio.h>
#endif
#include "common.h"
#include <alignalloc.h>
#include <quotearg.h>
#include <rmt.h>
#include <same-inode.h>
#include <stdarg.h>
/* Nonzero if we are verifying at the moment. */
@ -50,7 +44,8 @@ static char *diff_buffer;
void
diff_init (void)
{
diff_buffer = xalignalloc (getpagesize (), record_size);
void *ptr;
diff_buffer = page_aligned_alloc (&ptr, record_size);
if (listed_incremental_option)
read_directory_file ();
}
@ -77,20 +72,20 @@ report_difference (struct tar_stat_info *st, const char *fmt, ...)
}
/* Take a buffer returned by read_and_process and do nothing with it. */
static bool
process_noop (MAYBE_UNUSED idx_t size, MAYBE_UNUSED char *data)
static int
process_noop (MAYBE_UNUSED size_t size, MAYBE_UNUSED char *data)
{
return true;
return 1;
}
static bool
process_rawdata (idx_t bytes, char *buffer)
static int
process_rawdata (size_t bytes, char *buffer)
{
idx_t status = blocking_read (diff_handle, diff_buffer, bytes);
size_t status = blocking_read (diff_handle, diff_buffer, bytes);
if (status < bytes)
if (status != bytes)
{
if (errno)
if (status == SAFE_READ_ERROR)
{
read_error (current_stat_info.file_name);
report_difference (&current_stat_info, NULL);
@ -98,48 +93,53 @@ process_rawdata (idx_t bytes, char *buffer)
else
{
report_difference (&current_stat_info,
ngettext ("Could read only %td of %td byte",
"Could read only %td of %td bytes",
ngettext ("Could only read %lu of %lu byte",
"Could only read %lu of %lu bytes",
bytes),
status, bytes);
(unsigned long) status, (unsigned long) bytes);
}
return false;
return 0;
}
if (!memeq (buffer, diff_buffer, bytes))
if (memcmp (buffer, diff_buffer, bytes))
{
report_difference (&current_stat_info, _("Contents differ"));
return false;
return 0;
}
return true;
return 1;
}
/* Some other routine wants ST->stat.st_size bytes in the archive.
For each chunk of the archive, call PROCESSOR with the size of the chunk,
and the address of the chunk it can work with. PROCESSOR should return
true for success. Once it fails, continue skipping without calling
PROCESSOR anymore. */
/* Some other routine wants SIZE bytes in the archive. For each chunk
of the archive, call PROCESSOR with the size of the chunk, and the
address of the chunk it can work with. The PROCESSOR should return
nonzero for success. Once it returns error, continue skipping
without calling PROCESSOR anymore. */
static void
read_and_process (struct tar_stat_info *st, bool (*processor) (idx_t, char *))
read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
{
union block *data_block;
size_t data_size;
off_t size = st->stat.st_size;
mv_begin_read (st);
for (off_t size = st->stat.st_size; size; )
while (size)
{
union block *data_block = find_next_block ();
data_block = find_next_block ();
if (! data_block)
{
paxerror (0, _("Unexpected EOF in archive"));
ERROR ((0, 0, _("Unexpected EOF in archive")));
return;
}
idx_t data_size = available_space_after (data_block);
data_size = available_space_after (data_block);
if (data_size > size)
data_size = size;
if (!processor (data_size, charptr (data_block)))
if (!(*processor) (data_size, data_block->buffer))
processor = process_noop;
set_next_block_after (charptr (data_block) + data_size - 1);
set_next_block_after ((union block *)
(data_block->buffer + data_size - 1));
size -= data_size;
mv_size_left (size);
}
@ -148,18 +148,23 @@ read_and_process (struct tar_stat_info *st, bool (*processor) (idx_t, char *))
/* Call either stat or lstat over STAT_DATA, depending on
--dereference (-h), for a file which should exist. Diagnose any
problem. Return true for success, false otherwise. */
static bool
problem. Return nonzero for success, zero otherwise. */
static int
get_stat_data (char const *file_name, struct stat *stat_data)
{
if (deref_stat (file_name, stat_data) < 0)
int status = deref_stat (file_name, stat_data);
if (status != 0)
{
(errno == ENOENT ? stat_warn : stat_error) (file_name);
if (errno == ENOENT)
stat_warn (file_name);
else
stat_error (file_name);
report_difference (&current_stat_info, NULL);
return false;
return 0;
}
return true;
return 1;
}
@ -213,9 +218,7 @@ diff_file (void)
}
else
{
struct fdbase f = fdbase (file_name);
diff_handle = (f.fd == BADFD ? -1
: openat (f.fd, f.base, open_read_flags));
diff_handle = openat (chdir_fd, file_name, open_read_flags);
if (diff_handle < 0)
{
@ -225,6 +228,8 @@ diff_file (void)
}
else
{
int status;
if (current_stat_info.is_sparse)
sparse_diff_file (diff_handle, &current_stat_info);
else
@ -234,11 +239,13 @@ diff_file (void)
&& stat_data.st_size != 0)
{
struct timespec atime = get_stat_atime (&stat_data);
if (set_file_atime (diff_handle, f.fd, f.base, atime) < 0)
if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
!= 0)
utime_error (file_name);
}
if (close (diff_handle) < 0)
status = close (diff_handle);
if (status != 0)
close_error (file_name);
}
}
@ -253,22 +260,23 @@ diff_link (void)
if (get_stat_data (current_stat_info.file_name, &file_data)
&& get_stat_data (current_stat_info.link_name, &link_data)
&& !psame_inode (&file_data, &link_data))
&& !sys_compare_links (&file_data, &link_data))
report_difference (&current_stat_info,
_("Not linked to %s"),
quote_n_colon (QUOTE_ARG,
current_stat_info.link_name));
}
#ifdef HAVE_READLINK
static void
diff_symlink (void)
{
char buf[1024];
idx_t len = strlen (current_stat_info.link_name);
size_t len = strlen (current_stat_info.link_name);
char *linkbuf = len < sizeof buf ? buf : xmalloc (len + 1);
struct fdbase f = fdbase (current_stat_info.file_name);
ssize_t status = (f.fd == BADFD ? -1
: readlinkat (f.fd, f.base, linkbuf, len + 1));
ssize_t status = readlinkat (chdir_fd, current_stat_info.file_name,
linkbuf, len + 1);
if (status < 0)
{
@ -279,12 +287,13 @@ diff_symlink (void)
report_difference (&current_stat_info, NULL);
}
else if (status != len
|| !memeq (current_stat_info.link_name, linkbuf, len))
|| memcmp (current_stat_info.link_name, linkbuf, len) != 0)
report_difference (&current_stat_info, _("Symlink differs"));
if (linkbuf != buf)
free (linkbuf);
}
#endif
static void
diff_special (void)
@ -320,42 +329,37 @@ diff_special (void)
report_difference (&current_stat_info, _("Mode differs"));
}
/* Return zero if and only if A and B should be considered equal.
for the purposes of dump directory comparison. */
static char
static int
dumpdir_cmp (const char *a, const char *b)
{
size_t len;
while (*a)
switch (*a)
{
case 'Y':
case 'N':
/* If the null-terminated strings A and B are equal, other
than possibly A's first byte being 'Y' where B's is 'N' or
vice versa, advance A and B past the strings.
Otherwise, return 1. */
if (! (*b == 'Y' || *b == 'N'))
if (!strchr ("YN", *b))
return 1;
a++, b++;
FALLTHROUGH;
if (strcmp(a + 1, b + 1))
return 1;
len = strlen (a) + 1;
a += len;
b += len;
break;
case 'D':
/* If the null-terminated strings A and B are equal, advance A
and B past them. Otherwise, return 1. */
while (*a)
if (*a++ != *b++)
return 1;
if (*b)
if (strcmp(a, b))
return 1;
a++, b++;
len = strlen (a) + 1;
a += len;
b += len;
break;
case 'R':
case 'T':
case 'X':
return *b;
default:
unreachable ();
}
return *b;
}
@ -371,7 +375,7 @@ diff_dumpdir (struct tar_stat_info *dir)
int fd = subfile_open (dir->parent, dir->orig_file_name, open_read_flags);
if (fd < 0)
diag = open_diag;
else if (fstat (fd, &dir->stat) < 0)
else if (fstat (fd, &dir->stat))
{
diag = stat_diag;
close (fd);
@ -388,7 +392,7 @@ diff_dumpdir (struct tar_stat_info *dir)
if (dumpdir_buffer)
{
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer) != 0)
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer))
report_difference (dir, _("Contents differ"));
}
else
@ -398,13 +402,16 @@ diff_dumpdir (struct tar_stat_info *dir)
static void
diff_multivol (void)
{
struct stat stat_data;
int fd, status;
off_t offset;
if (current_stat_info.had_trailing_slash)
{
diff_dir ();
return;
}
struct stat stat_data;
if (!get_stat_data (current_stat_info.file_name, &stat_data))
return;
@ -415,11 +422,10 @@ diff_multivol (void)
return;
}
off_t offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
off_t file_size;
offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
if (offset < 0
|| ckd_add (&file_size, current_stat_info.stat.st_size, offset)
|| stat_data.st_size != file_size)
|| INT_ADD_OVERFLOW (current_stat_info.stat.st_size, offset)
|| stat_data.st_size != current_stat_info.stat.st_size + offset)
{
report_difference (&current_stat_info, _("Size differs"));
skip_member ();
@ -427,8 +433,7 @@ diff_multivol (void)
}
struct fdbase f = fdbase (current_stat_info.file_name);
int fd = f.fd == BADFD ? -1 : openat (f.fd, f.base, open_read_flags);
fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
if (fd < 0)
{
@ -446,7 +451,8 @@ diff_multivol (void)
else
read_and_process (&current_stat_info, process_rawdata);
if (close (fd) < 0)
status = close (fd);
if (status != 0)
close_error (current_stat_info.file_name);
}
@ -469,9 +475,9 @@ diff_archive (void)
switch (current_header->header.typeflag)
{
default:
paxerror (0, _("%s: Unknown file type '%c', diffed as normal file"),
quotearg_colon (current_stat_info.file_name),
current_header->header.typeflag);
ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
quotearg_colon (current_stat_info.file_name),
current_header->header.typeflag));
FALLTHROUGH;
case AREGTYPE:
case REGTYPE:
@ -490,9 +496,11 @@ diff_archive (void)
diff_link ();
break;
#ifdef HAVE_READLINK
case SYMTYPE:
diff_symlink ();
break;
#endif
case CHRTYPE:
case BLKTYPE:
@ -518,20 +526,22 @@ diff_archive (void)
void
verify_volume (void)
{
bool may_fail = false;
int may_fail = 0;
if (removed_prefixes_p ())
{
paxwarn (0,
_("Archive contains file names with leading prefixes removed."));
may_fail = true;
WARN((0, 0,
_("Archive contains file names with leading prefixes removed.")));
may_fail = 1;
}
if (transform_program_p ())
{
paxwarn (0, _("Archive contains transformed file names."));
may_fail = true;
WARN((0, 0,
_("Archive contains transformed file names.")));
may_fail = 1;
}
if (may_fail)
paxwarn (0, _("Verification may fail to locate original files."));
WARN((0, 0,
_("Verification may fail to locate original files.")));
clear_directory_table ();
@ -555,7 +565,7 @@ verify_volume (void)
ioctl (archive, FDFLUSH);
#endif
if (!mtioseek (true, -1) && rmtlseek (archive, 0, SEEK_SET) < 0)
if (!mtioseek (true, -1) && rmtlseek (archive, 0, SEEK_SET) != 0)
{
/* Lseek failed. Try a different method. */
seek_warn (archive_name_array[0]);
@ -574,7 +584,7 @@ verify_volume (void)
if (status == HEADER_FAILURE)
{
intmax_t counter = 0;
int counter = 0;
do
{
@ -585,11 +595,10 @@ verify_volume (void)
}
while (status == HEADER_FAILURE);
paxerror (0,
ngettext ("VERIFY FAILURE: %jd invalid header detected",
"VERIFY FAILURE: %jd invalid headers detected",
counter),
counter);
ERROR ((0, 0,
ngettext ("VERIFY FAILURE: %d invalid header detected",
"VERIFY FAILURE: %d invalid headers detected",
counter), counter));
}
if (status == HEADER_END_OF_FILE)
break;
@ -598,18 +607,20 @@ verify_volume (void)
set_next_block_after (current_header);
if (!ignore_zeros_option)
{
char buf[UINTMAX_STRSIZE_BOUND];
status = read_header (&current_header, &current_stat_info,
read_header_auto);
if (status == HEADER_ZERO_BLOCK)
break;
warnopt (WARN_ALONE_ZERO_BLOCK,
0, _("A lone zero block at %jd"),
intmax (current_block_ordinal ()));
WARNOPT (WARN_ALONE_ZERO_BLOCK,
(0, 0, _("A lone zero block at %s"),
STRINGIFY_BIGINT (current_block_ordinal (), buf)));
}
continue;
}
decode_header (current_header, &current_stat_info, &current_format, true);
decode_header (current_header, &current_stat_info, &current_format, 1);
diff_archive ();
tar_stat_destroy (&current_stat_info);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* Delete entries from a tar archive.
Copyright 1988-2026 Free Software Foundation, Inc.
Copyright 1988-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -23,9 +23,18 @@
#include <rmt.h>
static union block *new_record;
static idx_t new_blocks;
static int new_blocks;
static bool acting_as_filter;
/* FIXME: This module should not directly handle the following
variables, instead, the interface should be cleaned up. */
extern union block *record_start;
extern union block *record_end;
extern union block *current_block;
extern union block *recent_long_name;
extern union block *recent_long_link;
extern off_t records_read;
/* The number of records skipped at the start of the archive, when
passing over members that are not deleted. */
off_t records_skipped;
@ -52,12 +61,12 @@ move_archive (off_t count)
idx_t short_size = position0 % record_size;
idx_t start_offset = short_size ? record_size - short_size : 0;
off_t increment, move_start;
if (ckd_mul (&increment, record_size, count)
|| ckd_add (&move_start, position0, start_offset)
|| ckd_add (&position, move_start, increment)
if (INT_MULTIPLY_WRAPV (record_size, count, &increment)
|| INT_ADD_WRAPV (position0, start_offset, &move_start)
|| INT_ADD_WRAPV (move_start, increment, &position)
|| position < 0)
{
paxerror (EOVERFLOW, "lseek: %s", archive_name_array[0]);
ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0]));
return;
}
else if (rmtlseek (archive, position, SEEK_SET) == position)
@ -70,7 +79,7 @@ move_archive (off_t count)
/* Write out the record which has been filled. If MOVE_BACK_FLAG,
backspace to where we started. */
static void
write_record (bool move_back_flag)
write_record (int move_back_flag)
{
union block *save_record = record_start;
record_start = new_record;
@ -101,21 +110,22 @@ write_record (bool move_back_flag)
}
static void
write_recent_blocks (union block *h, idx_t blocks)
write_recent_blocks (union block *h, size_t blocks)
{
for (idx_t i = 0; i < blocks; i++)
size_t i;
for (i = 0; i < blocks; i++)
{
new_record[new_blocks++] = h[i];
if (new_blocks == blocking_factor)
write_record (true);
write_record (1);
}
}
static void
write_recent_bytes (char *data, idx_t bytes)
write_recent_bytes (char *data, size_t bytes)
{
idx_t blocks = bytes >> LG_BLOCKSIZE;
idx_t rest = bytes & (BLOCKSIZE - 1);
size_t blocks = bytes / BLOCKSIZE;
size_t rest = bytes - blocks * BLOCKSIZE;
write_recent_blocks ((union block *)data, blocks);
memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
@ -123,15 +133,17 @@ write_recent_bytes (char *data, idx_t bytes)
memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
new_blocks++;
if (new_blocks == blocking_factor)
write_record (true);
write_record (1);
}
static void
flush_file (void)
{
off_t blocks_to_skip;
set_next_block_after (current_header);
off_t size = current_stat_info.stat.st_size;
off_t blocks_to_skip = (size >> LG_BLOCKSIZE) + !!(size & (BLOCKSIZE - 1));
blocks_to_skip = (current_stat_info.stat.st_size
+ BLOCKSIZE - 1) / BLOCKSIZE;
while (record_end - current_block <= blocks_to_skip)
{
@ -139,7 +151,7 @@ flush_file (void)
flush_archive ();
if (record_end == current_block)
/* Hit EOF */
return;
break;
}
current_block += blocks_to_skip;
}
@ -153,11 +165,11 @@ delete_archive_members (void)
/* FIXME: Should clean the routine before cleaning these variables :-( */
struct name *name;
off_t blocks_to_keep = 0;
ptrdiff_t kept_blocks_in_record;
int kept_blocks_in_record;
name_gather ();
open_archive (ACCESS_UPDATE);
acting_as_filter = streq (archive_name_array[0], "-");
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
/* Skip to the first member that matches the name list. */
do
@ -178,7 +190,7 @@ delete_archive_members (void)
break;
}
name->found_count++;
if (!isfound (name))
if (!ISFOUND (name))
{
skim_member (acting_as_filter);
break;
@ -204,12 +216,12 @@ delete_archive_members (void)
switch (previous_status)
{
case HEADER_STILL_UNREAD:
paxwarn (0, _("This does not look like a tar archive"));
WARN ((0, 0, _("This does not look like a tar archive")));
FALLTHROUGH;
case HEADER_SUCCESS:
case HEADER_SUCCESS_EXTENDED:
case HEADER_ZERO_BLOCK:
paxerror (0, _("Skipping to next header"));
ERROR ((0, 0, _("Skipping to next header")));
FALLTHROUGH;
case HEADER_FAILURE:
break;
@ -270,7 +282,7 @@ delete_archive_members (void)
if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
{
name->found_count++;
if (isfound (name))
if (ISFOUND (name))
{
flush_file ();
break;
@ -293,11 +305,10 @@ delete_archive_members (void)
new_record[new_blocks] = *current_header;
new_blocks++;
blocks_to_keep
= ((current_stat_info.stat.st_size >> LG_BLOCKSIZE)
+ !!(current_stat_info.stat.st_size & (BLOCKSIZE - 1)));
= (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
set_next_block_after (current_header);
if (new_blocks == blocking_factor)
write_record (true);
write_record (1);
/* Copy data. */
@ -307,7 +318,7 @@ delete_archive_members (void)
while (blocks_to_keep)
{
ptrdiff_t count;
int count;
if (current_block == record_end)
{
@ -332,7 +343,7 @@ delete_archive_members (void)
kept_blocks_in_record -= count;
if (new_blocks == blocking_factor)
write_record (true);
write_record (1);
}
break;
@ -348,7 +359,7 @@ delete_archive_members (void)
break;
case HEADER_FAILURE:
paxerror (0, _("Deleting non-header from archive"));
ERROR ((0, 0, _("Deleting non-header from archive")));
set_next_block_after (current_header);
break;
@ -363,11 +374,11 @@ delete_archive_members (void)
/* Write the end of tape. FIXME: we can't use write_eot here,
as it gets confused when the input is at end of file. */
idx_t total_zero_blocks = 0;
int total_zero_blocks = 0;
do
{
idx_t zero_blocks = blocking_factor - new_blocks;
int zero_blocks = blocking_factor - new_blocks;
memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
total_zero_blocks += zero_blocks;
write_record (total_zero_blocks < 2);
@ -377,7 +388,7 @@ delete_archive_members (void)
if (! acting_as_filter && ! _isrmt (archive))
{
if (sys_truncate (archive) < 0)
if (sys_truncate (archive))
truncate_warn (archive_name_array[0]);
}
}

View File

@ -1,6 +1,6 @@
/* Per-directory exclusion files for tar.
Copyright 2014-2026 Free Software Foundation, Inc.
Copyright 2014-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <system.h>
#include <c-ctype.h>
#include <quotearg.h>
#include <flexmember.h>
#include <fnmatch.h>
@ -72,31 +71,36 @@ struct exclist
void
info_attach_exclist (struct tar_stat_info *dir)
{
struct exclist *head = NULL, *tail = NULL;
struct excfile *file;
struct exclist *head = NULL, *tail = NULL, *ent;
struct vcs_ignore_file *vcsfile;
if (dir->exclude_list)
return;
for (struct excfile *file = excfile_head; file; file = file->next)
for (file = excfile_head; file; file = file->next)
{
if (faccessat (dir->fd, file->name, F_OK, 0) == 0)
if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
{
FILE *fp;
struct exclude *ex = NULL;
int fd = subfile_open (dir, file->name, O_RDONLY);
if (fd < 0)
if (fd == -1)
{
open_error (file->name);
continue;
}
FILE *fp = fdopen (fd, "r");
fp = fdopen (fd, "r");
if (!fp)
{
paxerror (errno, _("%s: fdopen failed"), file->name);
ERROR ((0, errno, _("%s: fdopen failed"), file->name));
close (fd);
continue;
}
struct exclude *ex = new_exclude ();
if (!ex)
ex = new_exclude ();
struct vcs_ignore_file *vcsfile = get_vcs_ignore_file (file->name);
vcsfile = get_vcs_ignore_file (file->name);
if (vcsfile->initfn)
vcsfile->data = vcsfile->initfn (vcsfile->data);
@ -105,12 +109,16 @@ info_attach_exclist (struct tar_stat_info *dir)
FNM_FILE_NAME|EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED,
'\n',
vcsfile->data))
paxfatal (errno, "%s", quotearg_colon (file->name));
{
int e = errno;
FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
}
fclose (fp);
struct exclist *ent = xmalloc (sizeof *ent);
ent = xmalloc (sizeof (*ent));
ent->excluded = ex;
ent->flags = file->flags;
ent->flags = file->flags == EXCL_DEFAULT
? file->flags : vcsfile->flags;
ent->prev = tail;
ent->next = NULL;
@ -170,7 +178,12 @@ excluded_name (char const *name, struct tar_stat_info *st)
break;
if (!rname)
rname = name + dotslashlen (name);
{
rname = name;
/* Skip leading ./ */
while (*rname == '.' && ISSLASH (rname[1]))
rname += 2;
}
if ((result = excluded_file_name (ep->excluded, rname)))
break;
@ -191,13 +204,13 @@ cvs_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
struct wordsplit ws;
size_t i;
options |= EXCLUDE_ALLOC;
if (wordsplit (pattern, &ws,
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)
!= WRDSE_OK)
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS))
return;
for (idx_t i = 0; i < ws.ws_wordc; i++)
for (i = 0; i < ws.ws_wordc; i++)
add_exclude (ex, ws.ws_wordv[i], options);
wordsplit_free (&ws);
}
@ -206,7 +219,7 @@ static void
git_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (c_isspace (*pattern))
while (isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
@ -219,7 +232,7 @@ static void
bzr_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (c_isspace (*pattern))
while (isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
@ -254,25 +267,26 @@ static void
hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
{
int *hgopt = data;
size_t len;
while (c_isspace (*pattern))
while (isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
if (strncmp (pattern, "syntax:", 7) == 0)
{
for (pattern += 7; c_isspace (*pattern); ++pattern)
for (pattern += 7; isspace (*pattern); ++pattern)
;
if (streq (pattern, "regexp"))
if (strcmp (pattern, "regexp") == 0)
/* FIXME: Regexps must be perl-style */
*hgopt = EXCLUDE_REGEX;
else if (streq (pattern, "glob"))
else if (strcmp (pattern, "glob") == 0)
*hgopt = EXCLUDE_WILDCARDS;
/* Ignore unknown syntax */
return;
}
idx_t len = strlen (pattern);
len = strlen(pattern);
if (pattern[len-1] == '/')
{
char *p;
@ -306,7 +320,7 @@ get_vcs_ignore_file (const char *name)
struct vcs_ignore_file *p;
for (p = vcs_ignore_files; p->filename; p++)
if (streq (p->filename, name))
if (strcmp (p->filename, name) == 0)
break;
return p;
@ -318,5 +332,5 @@ exclude_vcs_ignores (void)
struct vcs_ignore_file *p;
for (p = vcs_ignore_files; p->filename; p++)
excfile_add (p->filename, p->flags);
excfile_add (p->filename, EXCL_DEFAULT);
}

View File

@ -1,6 +1,6 @@
/* Exit from GNU tar.
Copyright 2009-2026 Free Software Foundation, Inc.
Copyright 2009-2023 Free Software Foundation, Inc.
This file is part of GNU tar.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

119
src/map.c
View File

@ -1,6 +1,6 @@
/* Owner/group mapping for tar
Copyright 2015-2026 Free Software Foundation, Inc.
Copyright 2015-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -45,26 +45,28 @@ map_compare (void const *entry1, void const *entry2)
return map1->orig_id == map2->orig_id;
}
static bool
static int
parse_id (uintmax_t *retval,
char const *arg, char const *what, uintmax_t maxval,
char const *file, intmax_t line)
char const *file, unsigned line)
{
uintmax_t v;
char *p;
bool overflow;
*retval = stoint (arg, &p, &overflow, 0, maxval);
if ((p == arg) | *p)
errno = 0;
v = strtoumax (arg, &p, 10);
if (*p || errno)
{
error (0, 0, _("%s:%jd: invalid %s: %s"), file, line, what, arg);
return false;
error (0, 0, _("%s:%u: invalid %s: %s"), file, line, what, arg);
return -1;
}
if (overflow)
if (v > maxval)
{
error (0, 0, _("%s:%jd: %s out of range: %s"), file, line, what, arg);
return false;
error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
return -1;
}
return true;
*retval = v;
return 0;
}
static void
@ -78,9 +80,9 @@ map_read (Hash_table **ptab, char const *file,
ssize_t n;
struct wordsplit ws;
int wsopt;
intmax_t line;
bool err = false;
unsigned line;
int err = 0;
fp = fopen (file, "r");
if (!fp)
open_fatal (file);
@ -95,26 +97,26 @@ map_read (Hash_table **ptab, char const *file,
uintmax_t orig_id, new_id;
char *name = NULL;
char *colon;
++line;
if (wordsplit (buf, &ws, wsopt) != WRDSE_OK)
paxfatal (0, _("%s:%jd: cannot split line: %s"),
file, line, wordsplit_strerror (&ws));
if (wordsplit (buf, &ws, wsopt))
FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
file, line, wordsplit_strerror (&ws)));
wsopt |= WRDSF_REUSE;
if (ws.ws_wordc == 0)
continue;
if (ws.ws_wordc != 2)
{
error (0, 0, _("%s:%jd: malformed line"), file, line);
err = true;
error (0, 0, _("%s:%u: malformed line"), file, line);
err = 1;
continue;
}
if (ws.ws_wordv[0][0] == '+')
{
if (!parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
{
err = true;
err = 1;
continue;
}
}
@ -123,9 +125,9 @@ map_read (Hash_table **ptab, char const *file,
orig_id = name_to_id (ws.ws_wordv[0]);
if (orig_id == UINTMAX_MAX)
{
error (0, 0, _("%s:%jd: can't obtain %s of %s"),
error (0, 0, _("%s:%u: can't obtain %s of %s"),
file, line, what, ws.ws_wordv[0]);
err = true;
err = 1;
continue;
}
}
@ -136,17 +138,17 @@ map_read (Hash_table **ptab, char const *file,
if (colon > ws.ws_wordv[1])
name = ws.ws_wordv[1];
*colon++ = 0;
if (!parse_id (&new_id, colon, what, maxval, file, line))
if (parse_id (&new_id, colon, what, maxval, file, line))
{
err = true;
err = 1;
continue;
}
}
else if (ws.ws_wordv[1][0] == '+')
{
if (!parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
{
err = true;
err = 1;
continue;
}
}
@ -156,9 +158,9 @@ map_read (Hash_table **ptab, char const *file,
new_id = name_to_id (ws.ws_wordv[1]);
if (new_id == UINTMAX_MAX)
{
error (0, 0, _("%s:%jd: can't obtain %s of %s"),
error (0, 0, _("%s:%u: can't obtain %s of %s"),
file, line, what, ws.ws_wordv[1]);
err = true;
err = 1;
continue;
}
}
@ -167,10 +169,9 @@ map_read (Hash_table **ptab, char const *file,
ent->orig_id = orig_id;
ent->new_id = new_id;
ent->new_name = name ? xstrdup (name) : NULL;
if (!((*ptab
|| (*ptab = hash_initialize (0, NULL, map_hash, map_compare,
NULL)))
|| (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
&& hash_insert (*ptab, ent)))
xalloc_die ();
}
@ -178,7 +179,7 @@ map_read (Hash_table **ptab, char const *file,
wordsplit_free (&ws);
fclose (fp);
if (err)
paxfatal (0, _("errors reading map file"));
FATAL_ERROR ((0, 0, _("errors reading map file")));
}
/* UID translation */
@ -198,28 +199,37 @@ owner_map_read (char const *file)
map_read (&owner_map, file, name_to_uid, "UID", TYPE_MAXIMUM (uid_t));
}
void
int
owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
{
int rc = 1;
if (owner_map)
{
struct mapentry ent, *res;
ent.orig_id = uid;
res = hash_lookup (owner_map, &ent);
if (res)
{
*new_uid = res->new_id;
*new_name = res->new_name;
return;
return 0;
}
}
uid_t minus_1 = -1;
if (owner_option != minus_1)
*new_uid = owner_option;
if (owner_option != (uid_t) -1)
{
*new_uid = owner_option;
rc = 0;
}
if (owner_name_option)
*new_name = owner_name_option;
{
*new_name = owner_name_option;
rc = 0;
}
return rc;
}
/* GID translation */
@ -239,26 +249,35 @@ group_map_read (char const *file)
map_read (&group_map, file, name_to_gid, "GID", TYPE_MAXIMUM (gid_t));
}
void
int
group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
{
int rc = 1;
if (group_map)
{
struct mapentry ent, *res;
ent.orig_id = gid;
res = hash_lookup (group_map, &ent);
if (res)
{
*new_gid = res->new_id;
*new_name = res->new_name;
return;
return 0;
}
}
gid_t minus_1 = -1;
if (group_option != minus_1)
*new_gid = group_option;
if (group_option != (uid_t) -1)
{
*new_gid = group_option;
rc = 0;
}
if (group_name_option)
*new_name = group_name_option;
{
*new_name = group_name_option;
rc = 0;
}
return rc;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* Functions for dealing with sparse files
Copyright 2003-2026 Free Software Foundation, Inc.
Copyright 2003-2023 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 the
@ -16,7 +16,6 @@
with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
#include <c-ctype.h>
#include <inttostr.h>
#include <quotearg.h>
#include "common.h"
@ -41,8 +40,8 @@ struct tar_sparse_optab
bool (*decode_header) (struct tar_sparse_file *);
bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state,
void *);
bool (*dump_region) (struct tar_sparse_file *, idx_t);
bool (*extract_region) (struct tar_sparse_file *, idx_t);
bool (*dump_region) (struct tar_sparse_file *, size_t);
bool (*extract_region) (struct tar_sparse_file *, size_t);
};
struct tar_sparse_file
@ -74,8 +73,12 @@ dump_zeros (struct tar_sparse_file *file, off_t offset)
while (file->offset < offset)
{
idx_t size = min (BLOCKSIZE, offset - file->offset);
ssize_t wrbytes = write (file->fd, zero_buf, size);
size_t size = (BLOCKSIZE < offset - file->offset
? BLOCKSIZE
: offset - file->offset);
ssize_t wrbytes;
wrbytes = write (file->fd, zero_buf, size);
if (wrbytes <= 0)
{
if (wrbytes == 0)
@ -128,7 +131,7 @@ tar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state,
}
static bool
tar_sparse_dump_region (struct tar_sparse_file *file, idx_t i)
tar_sparse_dump_region (struct tar_sparse_file *file, size_t i)
{
if (file->optab->dump_region)
return file->optab->dump_region (file, i);
@ -136,7 +139,7 @@ tar_sparse_dump_region (struct tar_sparse_file *file, idx_t i)
}
static bool
tar_sparse_extract_region (struct tar_sparse_file *file, idx_t i)
tar_sparse_extract_region (struct tar_sparse_file *file, size_t i)
{
if (file->optab->extract_region)
return file->optab->extract_region (file, i);
@ -185,9 +188,9 @@ lseek_or_error (struct tar_sparse_file *file, off_t offset)
it's made *entirely* of zeros, returning a 0 the instant it finds
something that is a nonzero, i.e., useful data. */
static bool
zero_block_p (char const *buffer, idx_t size)
zero_block_p (char const *buffer, size_t size)
{
for (; size; size--)
while (size--)
if (*buffer++)
return false;
return true;
@ -197,10 +200,10 @@ static void
sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp)
{
struct sp_array *sparse_map = st->sparse_map;
idx_t avail = st->sparse_map_avail;
size_t avail = st->sparse_map_avail;
if (avail == st->sparse_map_size)
st->sparse_map = sparse_map =
xpalloc (sparse_map, &st->sparse_map_size, 1, -1, sizeof *sparse_map);
x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map);
sparse_map[avail] = *sp;
st->sparse_map_avail = avail + 1;
}
@ -212,6 +215,7 @@ sparse_scan_file_raw (struct tar_sparse_file *file)
struct tar_stat_info *st = file->stat_info;
int fd = file->fd;
char buffer[BLOCKSIZE];
size_t count = 0;
off_t offset = 0;
struct sp_array sp = {0, 0};
@ -220,17 +224,9 @@ sparse_scan_file_raw (struct tar_sparse_file *file)
if (!tar_sparse_scan (file, scan_begin, NULL))
return false;
while (true)
while ((count = blocking_read (fd, buffer, sizeof buffer)) != 0
&& count != SAFE_READ_ERROR)
{
idx_t count = blocking_read (fd, buffer, sizeof buffer);
if (count < sizeof buffer)
{
if (errno)
read_diag_details (st->orig_file_name, offset, sizeof buffer);
if (count == 0)
break;
}
/* Analyze the block. */
if (zero_block_p (buffer, count))
{
@ -253,8 +249,6 @@ sparse_scan_file_raw (struct tar_sparse_file *file)
}
offset += count;
if (count < sizeof buffer)
break;
}
/* save one more sparse segment of length 0 to indicate that
@ -263,6 +257,7 @@ sparse_scan_file_raw (struct tar_sparse_file *file)
sp.offset = offset;
sparse_add_map (st, &sp);
st->archive_file_size += count;
return tar_sparse_scan (file, scan_end, NULL);
}
@ -305,7 +300,7 @@ sparse_scan_file_seek (struct tar_sparse_file *file)
/* locate first chunk of data */
data_offset = lseek (fd, offset, SEEK_DATA);
if (data_offset < 0)
if (data_offset == (off_t)-1)
/* ENXIO == EOF; error otherwise */
{
if (errno == ENXIO)
@ -359,7 +354,8 @@ sparse_scan_file (struct tar_sparse_file *file)
return true;
#else
if (hole_detection == HOLE_DETECTION_SEEK)
paxwarn (0, _("\"seek\" hole detection is not supported, using \"raw\"."));
WARN((0, 0,
_("\"seek\" hole detection is not supported, using \"raw\".")));
/* fall back to "raw" for this and all other files */
hole_detection = HOLE_DETECTION_RAW;
#endif
@ -405,8 +401,9 @@ sparse_select_optab (struct tar_sparse_file *file)
}
static bool
sparse_dump_region (struct tar_sparse_file *file, idx_t i)
sparse_dump_region (struct tar_sparse_file *file, size_t i)
{
union block *blk;
off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
@ -414,50 +411,68 @@ sparse_dump_region (struct tar_sparse_file *file, idx_t i)
while (bytes_left > 0)
{
union block *blk = find_next_block ();
idx_t avail = available_space_after (blk);
idx_t bufsize = min (bytes_left, avail);
idx_t bytes_read = full_read (file->fd, charptr (blk), bufsize);
if (bytes_read < BLOCKSIZE)
memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
bytes_left -= bytes_read;
file->dumped_size += bytes_read;
size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
size_t bytes_read;
if (bytes_read < bufsize)
blk = find_next_block ();
bytes_read = full_read (file->fd, blk->buffer, bufsize);
if (bytes_read == SAFE_READ_ERROR)
{
off_t current_offset = (file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- bytes_left);
if (errno != 0)
read_diag_details (file->stat_info->orig_file_name,
current_offset, bufsize - bytes_read);
else
{
off_t cursize = current_offset;
struct stat st;
if (fstat (file->fd, &st) == 0 && st.st_size < cursize)
cursize = st.st_size;
intmax_t n = file->stat_info->stat.st_size - cursize;
warnopt (WARN_FILE_SHRANK, 0,
ngettext ("%s: File shrank by %jd byte; padding with zeros",
"%s: File shrank by %jd bytes; padding with zeros",
n),
quotearg_colon (file->stat_info->orig_file_name),
n);
}
if (! ignore_failed_read_option)
set_exit_status (TAREXIT_DIFFERS);
read_diag_details (file->stat_info->orig_file_name,
(file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- bytes_left),
bufsize);
return false;
}
else if (bytes_read == 0)
{
if (errno != 0)
{
read_diag_details (file->stat_info->orig_file_name,
(file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- bytes_left),
bufsize);
return false;
}
else
{
char buf[UINTMAX_STRSIZE_BOUND];
struct stat st;
size_t n;
if (fstat (file->fd, &st) == 0)
n = file->stat_info->stat.st_size - st.st_size;
else
n = file->stat_info->stat.st_size
- (file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- bytes_left);
set_next_block_after (charptr (blk) + bufsize - 1);
WARNOPT (WARN_FILE_SHRANK,
(0, 0,
ngettext ("%s: File shrank by %s byte; padding with zeros",
"%s: File shrank by %s bytes; padding with zeros",
n),
quotearg_colon (file->stat_info->orig_file_name),
STRINGIFY_BIGINT (n, buf)));
if (! ignore_failed_read_option)
set_exit_status (TAREXIT_DIFFERS);
return false;
}
}
memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
bytes_left -= bytes_read;
file->dumped_size += bytes_read;
set_next_block_after (blk);
}
return true;
}
static bool
sparse_extract_region (struct tar_sparse_file *file, idx_t i)
sparse_extract_region (struct tar_sparse_file *file, size_t i)
{
off_t write_size;
@ -474,17 +489,17 @@ sparse_extract_region (struct tar_sparse_file *file, idx_t i)
}
else while (write_size > 0)
{
size_t count;
size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size;
union block *blk = find_next_block ();
if (!blk)
{
paxerror (0, _("Unexpected EOF in archive"));
ERROR ((0, 0, _("Unexpected EOF in archive")));
return false;
}
idx_t avail = available_space_after (blk);
idx_t wrbytes = min (write_size, avail);
set_next_block_after (charptr (blk) + wrbytes - 1);
file->dumped_size += avail;
idx_t count = blocking_write (file->fd, charptr (blk), wrbytes);
set_next_block_after (blk);
file->dumped_size += BLOCKSIZE;
count = blocking_write (file->fd, blk->buffer, wrbytes);
write_size -= count;
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
file->offset += count;
@ -521,10 +536,12 @@ sparse_dump_file (int fd, struct tar_stat_info *st)
if (fd >= 0)
{
size_t i;
mv_begin_write (file.stat_info->file_name,
file.stat_info->stat.st_size,
file.stat_info->archive_file_size - file.dumped_size);
for (idx_t i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
rc = tar_sparse_dump_region (&file, i);
}
}
@ -560,6 +577,7 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
{
bool rc = true;
struct tar_sparse_file file;
size_t i;
if (!tar_sparse_init (&file))
{
@ -573,7 +591,7 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
file.offset = 0;
rc = tar_sparse_decode_header (&file);
for (idx_t i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
rc = tar_sparse_extract_region (&file, i);
*size = file.stat_info->archive_file_size - file.dumped_size;
return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
@ -605,14 +623,24 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
while (beg < end)
{
idx_t rdsize = min (end - beg, BLOCKSIZE);
size_t bytes_read;
size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
char diff_buffer[BLOCKSIZE];
idx_t bytes_read = full_read (file->fd, diff_buffer, rdsize);
if (bytes_read < rdsize)
bytes_read = full_read (file->fd, diff_buffer, rdsize);
if (bytes_read == SAFE_READ_ERROR)
{
if (errno)
read_diag_details (file->stat_info->orig_file_name, beg, rdsize);
read_diag_details (file->stat_info->orig_file_name,
beg,
rdsize);
return false;
}
else if (bytes_read == 0)
{
if (errno != 0)
read_diag_details (file->stat_info->orig_file_name,
beg,
rdsize);
else
report_difference (file->stat_info, _("Size differs"));
return false;
@ -634,7 +662,7 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
}
static bool
check_data_region (struct tar_sparse_file *file, idx_t i)
check_data_region (struct tar_sparse_file *file, size_t i)
{
off_t size_left;
@ -645,38 +673,47 @@ check_data_region (struct tar_sparse_file *file, idx_t i)
while (size_left > 0)
{
size_t bytes_read;
size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left;
char diff_buffer[BLOCKSIZE];
union block *blk = find_next_block ();
if (!blk)
{
paxerror (0, _("Unexpected EOF in archive"));
ERROR ((0, 0, _("Unexpected EOF in archive")));
return false;
}
set_next_block_after (blk);
file->dumped_size += BLOCKSIZE;
idx_t rdsize = min (size_left, BLOCKSIZE);
idx_t bytes_read = full_read (file->fd, diff_buffer, rdsize);
size_left -= bytes_read;
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
if (!memeq (blk->buffer, diff_buffer, bytes_read))
bytes_read = full_read (file->fd, diff_buffer, rdsize);
if (bytes_read == SAFE_READ_ERROR)
{
report_difference (file->stat_info, _("Contents differ"));
read_diag_details (file->stat_info->orig_file_name,
(file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- size_left),
rdsize);
return false;
}
if (bytes_read < rdsize)
else if (bytes_read == 0)
{
if (errno != 0)
read_diag_details (file->stat_info->orig_file_name,
(file->stat_info->sparse_map[i].offset
+ file->stat_info->sparse_map[i].numbytes
- size_left),
rdsize - bytes_read);
rdsize);
else
report_difference (&current_stat_info, _("Size differs"));
return false;
}
size_left -= bytes_read;
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
if (memcmp (blk->buffer, diff_buffer, bytes_read))
{
report_difference (file->stat_info, _("Contents differ"));
return false;
}
}
return true;
}
@ -686,6 +723,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
{
bool rc = true;
struct tar_sparse_file file;
size_t i;
off_t offset = 0;
if (!tar_sparse_init (&file))
@ -697,7 +735,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
rc = tar_sparse_decode_header (&file);
mv_begin_read (st);
for (idx_t i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
{
rc = check_sparse_region (&file,
offset, file.stat_info->sparse_map[i].offset)
@ -756,10 +794,9 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
return add_finish;
sp.offset = OFF_FROM_HEADER (s->offset);
sp.numbytes = OFF_FROM_HEADER (s->numbytes);
off_t size;
if (sp.offset < 0 || sp.numbytes < 0
|| ckd_add (&size, sp.offset, sp.numbytes)
|| file->stat_info->stat.st_size < size
|| INT_ADD_OVERFLOW (sp.offset, sp.numbytes)
|| file->stat_info->stat.st_size < sp.offset + sp.numbytes
|| file->stat_info->archive_file_size < 0)
return add_fail;
@ -782,43 +819,45 @@ oldgnu_fixup_header (struct tar_sparse_file *file)
static bool
oldgnu_get_sparse_info (struct tar_sparse_file *file)
{
size_t i;
union block *h = current_header;
int ext_p;
enum oldgnu_add_status rc;
file->stat_info->sparse_map_avail = 0;
for (idx_t i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
{
rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
if (rc != add_ok)
break;
}
for (char ext_p = h->oldgnu_header.isextended;
for (ext_p = h->oldgnu_header.isextended;
rc == add_ok && ext_p; ext_p = h->sparse_header.isextended)
{
h = find_next_block ();
if (!h)
{
paxerror (0, _("Unexpected EOF in archive"));
ERROR ((0, 0, _("Unexpected EOF in archive")));
return false;
}
set_next_block_after (h);
for (idx_t i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]);
}
if (rc == add_fail)
{
paxerror (0, _("%s: invalid sparse archive member"),
file->stat_info->orig_file_name);
ERROR ((0, 0, _("%s: invalid sparse archive member"),
file->stat_info->orig_file_name));
return false;
}
return true;
}
static void
oldgnu_store_sparse_info (struct tar_sparse_file *file, idx_t *pindex,
struct sparse *sp, idx_t sparse_size)
oldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex,
struct sparse *sp, size_t sparse_size)
{
for (; *pindex < file->stat_info->sparse_map_avail
&& sparse_size > 0; sparse_size--, sp++, ++*pindex)
@ -835,6 +874,7 @@ oldgnu_dump_header (struct tar_sparse_file *file)
{
off_t block_ordinal = current_block_ordinal ();
union block *blk;
size_t i;
blk = start_header (file->stat_info);
blk->header.typeflag = GNUTYPE_SPARSE;
@ -846,7 +886,7 @@ oldgnu_dump_header (struct tar_sparse_file *file)
/* Store the effective (shrunken) file size */
OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
idx_t i = 0;
i = 0;
oldgnu_store_sparse_info (file, &i,
blk->oldgnu_header.sp,
SPARSES_IN_OLDGNU_HEADER);
@ -903,8 +943,9 @@ star_fixup_header (struct tar_sparse_file *file)
static bool
star_get_sparse_info (struct tar_sparse_file *file)
{
size_t i;
union block *h = current_header;
char ext_p;
int ext_p;
enum oldgnu_add_status rc = add_ok;
file->stat_info->sparse_map_avail = 0;
@ -913,7 +954,7 @@ star_get_sparse_info (struct tar_sparse_file *file)
&& h->star_in_header.sp[0].offset[10] != '\0')
{
/* Old star format */
for (idx_t i = 0; i < SPARSES_IN_STAR_HEADER; i++)
for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
{
rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
if (rc != add_ok)
@ -929,19 +970,19 @@ star_get_sparse_info (struct tar_sparse_file *file)
h = find_next_block ();
if (!h)
{
paxerror (0, _("Unexpected EOF in archive"));
ERROR ((0, 0, _("Unexpected EOF in archive")));
return false;
}
set_next_block_after (h);
for (idx_t i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]);
file->dumped_size += BLOCKSIZE;
}
if (rc == add_fail)
{
paxerror (0, _("%s: invalid sparse archive member"),
file->stat_info->orig_file_name);
ERROR ((0, 0, _("%s: invalid sparse archive member"),
file->stat_info->orig_file_name));
return false;
}
return true;
@ -1061,6 +1102,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
{
off_t block_ordinal = current_block_ordinal ();
union block *blk;
size_t i;
char nbuf[UINTMAX_STRSIZE_BOUND];
struct sp_array *map = file->stat_info->sparse_map;
char *save_file_name = NULL;
@ -1072,7 +1114,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
if (xheader_keyword_deleted_p ("GNU.sparse.map")
|| tar_sparse_minor == 0)
{
for (idx_t i = 0; i < file->stat_info->sparse_map_avail; i++)
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
{
xheader_store ("GNU.sparse.offset", file->stat_info, &i);
xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
@ -1086,7 +1128,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
"%d/GNUSparseFile.%p/%f", 0);
xheader_string_begin (&file->stat_info->xhdr);
for (idx_t i = 0; i < file->stat_info->sparse_map_avail; i++)
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
{
if (i)
xheader_string_add (&file->stat_info->xhdr, ",");
@ -1114,65 +1156,48 @@ pax_dump_header_0 (struct tar_sparse_file *file)
return true;
}
/* An output block BLOCK, and a pointer PTR into it. */
struct block_ptr
{
union block *block;
char *ptr;
};
/* Append to BP the contents of the string SRC, followed by a newline.
If the string doesn't fit, put any overflow into the succeeding blocks.
Return the updated BP. */
static struct block_ptr
dump_str_nl (struct block_ptr bp, char const *str)
{
char *endp = bp.block->buffer + BLOCKSIZE;
char c;
do
{
c = *str++;
if (bp.ptr == endp)
{
set_next_block_after (bp.block);
bp.block = find_next_block ();
bp.ptr = bp.block->buffer;
endp = bp.block->buffer + BLOCKSIZE;
}
*bp.ptr++ = c ? c : '\n';
}
while (c);
return bp;
}
/* Return the floor of the log base 10 of N. If N is 0, return 0. */
static int
floorlog10 (uintmax_t n)
{
for (int f = 0; ; f++)
if ((n /= 10) == 0)
return f;
}
static bool
pax_dump_header_1 (struct tar_sparse_file *file)
{
off_t block_ordinal = current_block_ordinal ();
union block *blk;
char *p, *q;
size_t i;
char nbuf[UINTMAX_STRSIZE_BOUND];
off_t size = 0;
struct sp_array *map = file->stat_info->sparse_map;
char *save_file_name = file->stat_info->file_name;
#define COPY_STRING(b,dst,src) do \
{ \
char *endp = b->buffer + BLOCKSIZE; \
char const *srcp = src; \
while (*srcp) \
{ \
if (dst == endp) \
{ \
set_next_block_after (b); \
b = find_next_block (); \
dst = b->buffer; \
endp = b->buffer + BLOCKSIZE; \
} \
*dst++ = *srcp++; \
} \
} while (0)
/* Compute stored file size */
off_t size = floorlog10 (file->stat_info->sparse_map_avail) + 2;
for (idx_t i = 0; i < file->stat_info->sparse_map_avail; i++)
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
size += strlen (p) + 1;
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
{
size += floorlog10 (map[i].offset) + 2;
size += floorlog10 (map[i].numbytes) + 2;
p = umaxtostr (map[i].offset, nbuf);
size += strlen (p) + 1;
p = umaxtostr (map[i].numbytes, nbuf);
size += strlen (p) + 1;
}
size = (size + BLOCKSIZE - 1) & ~(BLOCKSIZE - 1);
file->stat_info->archive_file_size += size;
file->dumped_size += size;
size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
file->stat_info->archive_file_size += size * BLOCKSIZE;
file->dumped_size += size * BLOCKSIZE;
/* Store sparse file identification */
xheader_store ("GNU.sparse.major", file->stat_info, NULL);
@ -1186,22 +1211,27 @@ pax_dump_header_1 (struct tar_sparse_file *file)
if (strlen (file->stat_info->file_name) > NAME_FIELD_SIZE)
file->stat_info->file_name[NAME_FIELD_SIZE] = 0;
struct block_ptr bp;
bp.block = pax_start_header (file->stat_info);
finish_header (file->stat_info, bp.block, block_ordinal);
blk = pax_start_header (file->stat_info);
finish_header (file->stat_info, blk, block_ordinal);
free (file->stat_info->file_name);
file->stat_info->file_name = save_file_name;
bp.block = find_next_block ();
bp.ptr = bp.block->buffer;
bp = dump_str_nl (bp, umaxtostr (file->stat_info->sparse_map_avail, nbuf));
for (idx_t i = 0; i < file->stat_info->sparse_map_avail; i++)
blk = find_next_block ();
q = blk->buffer;
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
COPY_STRING (blk, q, p);
COPY_STRING (blk, q, "\n");
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
{
bp = dump_str_nl (bp, umaxtostr (map[i].offset, nbuf));
bp = dump_str_nl (bp, umaxtostr (map[i].numbytes, nbuf));
p = umaxtostr (map[i].offset, nbuf);
COPY_STRING (blk, q, p);
COPY_STRING (blk, q, "\n");
p = umaxtostr (map[i].numbytes, nbuf);
COPY_STRING (blk, q, p);
COPY_STRING (blk, q, "\n");
}
memset (bp.ptr, 0, BLOCKSIZE - (bp.ptr - bp.block->buffer));
set_next_block_after (bp.block);
memset (q, 0, BLOCKSIZE - (q - blk->buffer));
set_next_block_after (blk);
return true;
}
@ -1215,52 +1245,23 @@ pax_dump_header (struct tar_sparse_file *file)
pax_dump_header_0 (file) : pax_dump_header_1 (file);
}
/* A success flag OK, a computed integer N, and block + ptr BP. */
struct ok_n_block_ptr
static bool
decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
{
bool ok;
uintmax_t n;
struct block_ptr bp;
};
uintmax_t u;
char *arg_lim;
static struct ok_n_block_ptr
decode_num (struct block_ptr bp, uintmax_t nmax, struct tar_sparse_file *file)
{
char *endp = bp.block->buffer + BLOCKSIZE;
uintmax_t n = 0;
bool digit_seen = false, nondigit_seen = false, overflow = false;
while (true)
{
if (bp.ptr == endp)
{
set_next_block_after (bp.block);
bp.block = find_next_block ();
if (!bp.block)
paxfatal (0, _("Unexpected EOF in archive"));
bp.ptr = bp.block->buffer;
endp = bp.block->buffer + BLOCKSIZE;
}
char c = *bp.ptr++;
if (c == '\n')
break;
if (c_isdigit (c))
{
digit_seen = true;
overflow |= ckd_mul (&n, n, 10);
overflow |= ckd_add (&n, n, c - '0');
}
else
nondigit_seen = true;
}
if (!ISDIGIT (*arg))
return false;
overflow |= nmax < n;
char const *msgid
= ((!digit_seen | nondigit_seen) ? N_("%s: malformed sparse archive member")
: overflow ? N_("%s: numeric overflow in sparse archive member")
: NULL);
if (msgid)
paxerror (0, gettext (msgid), file->stat_info->orig_file_name);
return (struct ok_n_block_ptr) { .ok = !msgid, .n = n, .bp = bp };
errno = 0;
u = strtoumax (arg, &arg_lim, 10);
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
return false;
*num = u;
return true;
}
static bool
@ -1268,38 +1269,82 @@ pax_decode_header (struct tar_sparse_file *file)
{
if (file->stat_info->sparse_major > 0)
{
off_t start = current_block_ordinal ();
uintmax_t u;
char nbuf[UINTMAX_STRSIZE_BOUND];
union block *blk;
char *p;
size_t i;
off_t start;
#define COPY_BUF(b,buf,src) do \
{ \
char *endp = b->buffer + BLOCKSIZE; \
char *dst = buf; \
do \
{ \
if (dst == buf + UINTMAX_STRSIZE_BOUND -1) \
{ \
ERROR ((0, 0, _("%s: numeric overflow in sparse archive member"), \
file->stat_info->orig_file_name)); \
return false; \
} \
if (src == endp) \
{ \
set_next_block_after (b); \
b = find_next_block (); \
if (!b) \
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive"))); \
src = b->buffer; \
endp = b->buffer + BLOCKSIZE; \
} \
*dst = *src++; \
} \
while (*dst++ != '\n'); \
dst[-1] = 0; \
} while (0)
start = current_block_ordinal ();
set_next_block_after (current_header);
struct block_ptr bp;
bp.block = find_next_block ();
if (!bp.block)
paxfatal (0, _("Unexpected EOF in archive"));
bp.ptr = bp.block->buffer;
struct ok_n_block_ptr onbp = decode_num (bp, SIZE_MAX, file);
if (!onbp.ok)
return false;
bp = onbp.bp;
file->stat_info->sparse_map_size = onbp.n;
file->stat_info->sparse_map
= xicalloc (file->stat_info->sparse_map_size,
sizeof *file->stat_info->sparse_map);
blk = find_next_block ();
if (!blk)
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
p = blk->buffer;
COPY_BUF (blk,nbuf,p);
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
{
ERROR ((0, 0, _("%s: malformed sparse archive member"),
file->stat_info->orig_file_name));
return false;
}
file->stat_info->sparse_map_size = u;
file->stat_info->sparse_map = xcalloc (file->stat_info->sparse_map_size,
sizeof (*file->stat_info->sparse_map));
file->stat_info->sparse_map_avail = 0;
for (idx_t i = 0; i < file->stat_info->sparse_map_size; i++)
for (i = 0; i < file->stat_info->sparse_map_size; i++)
{
struct sp_array sp;
onbp = decode_num (bp, file->stat_info->stat.st_size, file);
if (!onbp.ok)
return false;
sp.offset = onbp.n;
off_t numbytes_max = file->stat_info->stat.st_size - sp.offset;
onbp = decode_num (onbp.bp, numbytes_max, file);
if (!onbp.ok)
return false;
sp.numbytes = onbp.n;
bp = onbp.bp;
COPY_BUF (blk,nbuf,p);
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
{
ERROR ((0, 0, _("%s: malformed sparse archive member"),
file->stat_info->orig_file_name));
return false;
}
sp.offset = u;
COPY_BUF (blk,nbuf,p);
if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t))
|| INT_ADD_OVERFLOW (sp.offset, u)
|| file->stat_info->stat.st_size < sp.offset + u)
{
ERROR ((0, 0, _("%s: malformed sparse archive member"),
file->stat_info->orig_file_name));
return false;
}
sp.numbytes = u;
sparse_add_map (file->stat_info, &sp);
}
set_next_block_after (bp.block);
set_next_block_after (blk);
file->dumped_size += BLOCKSIZE * (current_block_ordinal () - start);
}

View File

@ -1,5 +1,5 @@
/* This file is part of GNU tar.
Copyright 2007-2026 Free Software Foundation, Inc.
Copyright 2007-2023 Free Software Foundation, Inc.
Written by Sergey Poznyakoff.
@ -19,23 +19,18 @@
#include <system.h>
#include "common.h"
#include <stdcountof.h>
struct compression_suffix
{
char suffix[sizeof "tbz2"]; /* "tbz2" is tied for longest. */
char program[max (max (max (sizeof GZIP_PROGRAM, sizeof COMPRESS_PROGRAM),
max (sizeof BZIP2_PROGRAM, sizeof LZIP_PROGRAM)),
max (max (sizeof LZMA_PROGRAM, sizeof LZOP_PROGRAM),
max (sizeof XZ_PROGRAM, sizeof ZSTD_PROGRAM)))];
const char *suffix;
size_t length;
const char *program;
};
static struct compression_suffix const compression_suffixes[] = {
static struct compression_suffix compression_suffixes[] = {
#define __CAT2__(a,b) a ## b
#define S(s, p) #s, __CAT2__(p,_PROGRAM)
{ "tar", "" },
#define S(s,p) #s, sizeof (#s) - 1, __CAT2__(p,_PROGRAM)
{ "tar", 3, NULL },
{ S(gz, GZIP) },
{ S(z, GZIP) },
{ S(tgz, GZIP) },
{ S(taz, GZIP) },
{ S(Z, COMPRESS) },
@ -48,71 +43,63 @@ static struct compression_suffix const compression_suffixes[] = {
{ S(lzma, LZMA) },
{ S(tlz, LZMA) },
{ S(lzo, LZOP) },
{ S(tzo, LZOP) },
{ S(xz, XZ) },
{ S(txz, XZ) }, /* Slackware */
{ S(zst, ZSTD) },
{ S(tzst, ZSTD) },
{ NULL }
#undef S
#undef __CAT2__
};
/* Extract the suffix from archive file NAME, and return a pointer to
compression_suffix associated with it or NULL if none is found.
No matter what is the return value, if RET_LEN is not NULL, store
there the length of NAME with that suffix stripped, or 0 if NAME has
no suffix. */
static struct compression_suffix const *
find_compression_suffix (char const *name, idx_t *ret_len)
find_compression_suffix (const char *name, size_t *ret_len)
{
char const *suf = strrchr (name, '.');
char *suf = strrchr (name, '.');
if (suf && suf[1] != 0 && suf[1] != '/')
if (suf)
{
if (ret_len)
*ret_len = suf - name;
suf++;
size_t len;
struct compression_suffix *p;
for (struct compression_suffix const *p = compression_suffixes;
p < compression_suffixes + countof (compression_suffixes);
p++)
if (streq (p->suffix, suf))
return p;
suf++;
len = strlen (suf);
for (p = compression_suffixes; p->suffix; p++)
{
if (p->length == len && memcmp (p->suffix, suf, len) == 0)
{
if (ret_len)
*ret_len = strlen (name) - len - 1;
return p;
}
}
}
else if (ret_len)
*ret_len = 0;
return NULL;
}
/* Select compression program using the suffix of the archive file NAME.
Use DEFPROG, if there is no suffix, or if no program is associated with
the suffix. In the latter case, if VERBOSE is true, issue a warning.
*/
void
set_compression_program_by_suffix (const char *name, const char *defprog,
bool verbose)
static const char *
find_compression_program (const char *name, const char *defprog)
{
idx_t len;
struct compression_suffix const *p = find_compression_suffix (name, &len);
struct compression_suffix const *p = find_compression_suffix (name, NULL);
if (p)
use_compress_program_option = p->program[0] ? p->program : NULL;
else
{
use_compress_program_option = defprog;
if (len > 0 && verbose)
paxwarn (0,
_("no compression program is defined for suffix '%s';"
" assuming %s"),
name + len,
defprog ? defprog : "uncompressed archive");
}
return p->program;
return defprog;
}
void
set_compression_program_by_suffix (const char *name, const char *defprog)
{
const char *program = find_compression_program (name, defprog);
if (program)
use_compress_program_option = program;
}
char *
strip_compression_suffix (const char *name)
{
char *s = NULL;
idx_t len;
size_t len;
struct compression_suffix const *p = find_compression_suffix (name, &len);
if (p)

View File

@ -1,6 +1,6 @@
/* System-dependent calls for tar.
Copyright 2003-2026 Free Software Foundation, Inc.
Copyright 2003-2023 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 the
@ -16,22 +16,13 @@
with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
#if HAVE_SYS_MTIO_H
# include <sys/ioctl.h>
# include <sys/mtio.h>
#endif
#include <system-ioctl.h>
#include "common.h"
#include <priv-set.h>
#include <rmt.h>
#include <same-inode.h>
#include <signal.h>
#include <wordsplit.h>
#include <poll.h>
#include <parse-datetime.h>
bool dev_null_output;
static _Noreturn void
xexec (const char *cmd)
@ -63,8 +54,8 @@ mtioseek (bool count_files, off_t count)
? (count < 0 ? MTBSF : MTFSF)
: (count < 0 ? MTBSR : MTFSR));
if (! (count < 0
? ckd_sub (&operation.mt_count, 0, count)
: ckd_add (&operation.mt_count, count, 0))
? INT_SUBTRACT_WRAPV (0, count, &operation.mt_count)
: INT_ADD_WRAPV (count, 0, &operation.mt_count))
&& (0 <= rmtioctl (archive, MTIOCTOP, &operation)
|| (errno == EIO
&& 0 <= rmtioctl (archive, MTIOCTOP, &operation))))
@ -76,7 +67,7 @@ mtioseek (bool count_files, off_t count)
return false;
}
#if !HAVE_WAITPID /* MingW, MSVC 14. */
#if MSDOS
bool
sys_get_archive_stat (void)
@ -95,7 +86,7 @@ sys_detect_dev_null_output (void)
{
static char const dev_null[] = "nul";
dev_null_output = (streq (archive_name_array[0], dev_null)
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|| (! _isrmt (archive)));
}
@ -126,43 +117,42 @@ sys_compare_gid (struct stat *a, struct stat *b)
return true;
}
void
sys_compare_links (struct stat *link_data, struct stat *stat_data)
{
return true;
}
int
sys_truncate (int fd)
{
return write (fd, "", 0);
}
idx_t
size_t
sys_write_archive_buffer (void)
{
return full_write (archive, charptr (record_start), record_size);
return full_write (archive, record_start->buffer, record_size);
}
/* Set ARCHIVE for writing, then compressing an archive. */
void
sys_child_open_for_compress (void)
{
paxfatal (0, _("Cannot use compressed or remote archives"));
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
}
/* Set ARCHIVE for uncompressing, then reading an archive. */
void
sys_child_open_for_uncompress (void)
{
paxfatal (0, _("Cannot use compressed or remote archives"));
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
}
bool
sys_exec_setmtime_script (const char *script_name,
int dirfd,
const char *file_name,
const char *fmt,
struct timespec *ts)
{
paxfatal (0, _("--set-mtime-command not implemented on this platform"));
}
#else
extern union block *record_start; /* FIXME */
bool
sys_get_archive_stat (void)
{
@ -189,23 +179,24 @@ bool
sys_file_is_archive (struct tar_stat_info *p)
{
return (!dev_null_output && !_isrmt (archive)
&& psame_inode (&p->stat, &archive_stat));
&& p->stat.st_dev == archive_stat.st_dev
&& p->stat.st_ino == archive_stat.st_ino);
}
static char const dev_null[] = "/dev/null";
/* Detect if outputting to "/dev/null". */
void
sys_detect_dev_null_output (void)
{
static char const dev_null[] = "/dev/null";
static struct stat dev_null_stat;
dev_null_output = (streq (archive_name_array[0], dev_null)
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|| (! _isrmt (archive)
&& S_ISCHR (archive_stat.st_mode)
&& (dev_null_stat.st_ino != 0
|| stat (dev_null, &dev_null_stat) == 0)
&& psame_inode (&archive_stat, &dev_null_stat)));
&& archive_stat.st_ino == dev_null_stat.st_ino
&& archive_stat.st_dev == dev_null_stat.st_dev));
}
void
@ -215,7 +206,7 @@ sys_wait_for_child (pid_t child_pid, bool eof)
{
int wait_status;
while (waitpid (child_pid, &wait_status, 0) < 0)
while (waitpid (child_pid, &wait_status, 0) == -1)
if (errno != EINTR)
{
waitpid_error (use_compress_program_option);
@ -226,11 +217,11 @@ sys_wait_for_child (pid_t child_pid, bool eof)
{
int sig = WTERMSIG (wait_status);
if (!(!eof && sig == SIGPIPE))
paxfatal (0, _("Child died with signal %d"), sig);
FATAL_ERROR ((0, 0, _("Child died with signal %d"), sig));
}
else if (WEXITSTATUS (wait_status) != 0)
paxfatal (0, _("Child returned status %d"),
WEXITSTATUS (wait_status));
FATAL_ERROR ((0, 0, _("Child returned status %d"),
WEXITSTATUS (wait_status)));
}
}
@ -251,7 +242,7 @@ sys_spawn_shell (void)
else
{
int wait_status;
while (waitpid (child, &wait_status, 0) < 0)
while (waitpid (child, &wait_status, 0) == -1)
if (errno != EINTR)
{
waitpid_error (shell);
@ -272,48 +263,47 @@ sys_compare_gid (struct stat *a, struct stat *b)
return a->st_gid == b->st_gid;
}
bool
sys_compare_links (struct stat *link_data, struct stat *stat_data)
{
return stat_data->st_dev == link_data->st_dev
&& stat_data->st_ino == link_data->st_ino;
}
int
sys_truncate (int fd)
{
off_t pos = lseek (fd, 0, SEEK_CUR);
off_t pos = lseek (fd, (off_t) 0, SEEK_CUR);
return pos < 0 ? -1 : ftruncate (fd, pos);
}
/* Return true if NAME is the name of a regular file, or if the file
/* Return nonzero if NAME is the name of a regular file, or if the file
does not exist (so it would be created as a regular file). */
static bool
static int
is_regular_file (const char *name)
{
struct stat stbuf;
if (stat (name, &stbuf) == 0)
return !!S_ISREG (stbuf.st_mode);
return S_ISREG (stbuf.st_mode);
else
return errno == ENOENT;
}
idx_t
size_t
sys_write_archive_buffer (void)
{
return rmtwrite (archive, charptr (record_start), record_size);
return rmtwrite (archive, record_start->buffer, record_size);
}
/* Read and write file descriptors from a pipe(pipefd) call. */
enum { PREAD, PWRITE };
#define PREAD 0 /* read file descriptor from pipe() */
#define PWRITE 1 /* write file descriptor from pipe() */
/* Work around GCC bug 109839. */
#if 13 <= __GNUC__
# pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
#endif
/* Close file having descriptor FD, and abort if close unsuccessful. */
static void
xclose (int fd)
{
if (close (fd) < 0)
close_error (_("(pipe)"));
}
/* Duplicate file descriptor FROM into becoming INTO.
INTO is closed first and has to be the next available slot. */
static void
@ -322,7 +312,10 @@ xdup2 (int from, int into)
if (from != into)
{
if (dup2 (from, into) < 0)
paxfatal (errno, _("Cannot dup2"));
{
int e = errno;
FATAL_ERROR ((0, e, _("Cannot dup2")));
}
xclose (from);
}
}
@ -334,7 +327,7 @@ wait_for_grandchild (pid_t pid)
int wait_status;
int exit_code = 0;
while (waitpid (pid, &wait_status, 0) < 0)
while (waitpid (pid, &wait_status, 0) == -1)
if (errno != EINTR)
{
waitpid_error (use_compress_program_option);
@ -391,7 +384,7 @@ sys_child_open_for_compress (void)
/* We don't need a grandchild tar. Open the archive and launch the
compressor. */
if (!streq (archive_name_array[0], "-"))
if (strcmp (archive_name_array[0], "-"))
{
archive = creat (archive_name_array[0], MODE_RW);
if (archive < 0)
@ -433,7 +426,7 @@ sys_child_open_for_compress (void)
xdup2 (child_pipe[PREAD], STDIN_FILENO);
xclose (child_pipe[PWRITE]);
if (streq (archive_name_array[0], "-"))
if (strcmp (archive_name_array[0], "-") == 0)
archive = STDOUT_FILENO;
else
{
@ -446,20 +439,20 @@ sys_child_open_for_compress (void)
while (1)
{
ptrdiff_t status = 0;
size_t status = 0;
char *cursor;
idx_t length;
size_t length;
/* Assemble a record. */
for (length = 0, cursor = charptr (record_start);
for (length = 0, cursor = record_start->buffer;
length < record_size;
length += status, cursor += status)
{
idx_t size = record_size - length;
size_t size = record_size - length;
status = safe_read (STDIN_FILENO, cursor, size);
if (status < 0)
if (status == SAFE_READ_ERROR)
read_fatal (use_compress_program_option);
if (status == 0)
break;
@ -475,7 +468,7 @@ sys_child_open_for_compress (void)
if (length > 0)
{
memset (charptr (record_start) + length, 0, record_size - length);
memset (record_start->buffer + length, 0, record_size - length);
status = sys_write_archive_buffer ();
if (status != record_size)
archive_write_error (status);
@ -508,22 +501,24 @@ run_decompress_program (void)
{
if (prog)
{
warnopt (WARN_DECOMPRESS_PROGRAM, errno, _("cannot run %s"), prog);
warnopt (WARN_DECOMPRESS_PROGRAM, 0, _("trying %s"), p);
WARNOPT (WARN_DECOMPRESS_PROGRAM,
(0, errno, _("cannot run %s"), prog));
WARNOPT (WARN_DECOMPRESS_PROGRAM,
(0, 0, _("trying %s"), p));
}
if (wordsplit (p, &ws, wsflags) != WRDSE_OK)
paxfatal (0, _("cannot split string '%s': %s"),
p, wordsplit_strerror (&ws));
if (wordsplit (p, &ws, wsflags))
FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
p, wordsplit_strerror (&ws)));
wsflags |= WRDSF_REUSE;
memmove (ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
ws.ws_wordc * sizeof *ws.ws_wordv);
memmove(ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
sizeof(ws.ws_wordv[0])*ws.ws_wordc);
ws.ws_wordv[ws.ws_wordc] = (char *) "-d";
prog = p;
execvp (ws.ws_wordv[0], ws.ws_wordv);
ws.ws_wordv[ws.ws_wordc] = NULL;
}
if (!prog)
paxfatal (0, _("unable to run decompression program"));
FATAL_ERROR ((0, 0, _("unable to run decompression program")));
exec_fatal (prog);
}
@ -561,7 +556,7 @@ sys_child_open_for_uncompress (void)
b) the file is to be accessed by rmt: compressor doesn't know how;
c) the file is not a plain file. */
if (!streq (archive_name_array[0], "-")
if (strcmp (archive_name_array[0], "-") != 0
&& !_remdev (archive_name_array[0])
&& is_regular_file (archive_name_array[0]))
{
@ -601,7 +596,7 @@ sys_child_open_for_uncompress (void)
xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
xclose (child_pipe[PREAD]);
if (streq (archive_name_array[0], "-"))
if (strcmp (archive_name_array[0], "-") == 0)
archive = STDIN_FILENO;
else
archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
@ -611,26 +606,34 @@ sys_child_open_for_uncompress (void)
/* Let's read the archive and pipe it into stdout. */
while (true)
while (1)
{
char *cursor;
size_t maximum;
size_t count;
size_t status;
clear_read_error_count ();
ptrdiff_t n;
while ((n = rmtread (archive, charptr (record_start), record_size)) < 0)
archive_read_error ();
if (n == 0)
break;
char *cursor = charptr (record_start);
do
error_loop:
status = rmtread (archive, record_start->buffer, record_size);
if (status == SAFE_READ_ERROR)
{
idx_t count = min (n, BLOCKSIZE);
archive_read_error ();
goto error_loop;
}
if (status == 0)
break;
cursor = record_start->buffer;
maximum = status;
while (maximum)
{
count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE;
if (full_write (STDOUT_FILENO, cursor, count) != count)
write_error (use_compress_program_option);
cursor += count;
n -= count;
maximum -= count;
}
while (n);
}
xclose (STDOUT_FILENO);
@ -643,8 +646,11 @@ sys_child_open_for_uncompress (void)
static void
dec_to_env (char const *envar, uintmax_t num)
{
char numstr[UINTMAX_STRSIZE_BOUND];
if (setenv (envar, umaxtostr (num, numstr), 1) < 0)
char buf[UINTMAX_STRSIZE_BOUND];
char *numstr;
numstr = STRINGIFY_BIGINT (num, buf);
if (setenv (envar, numstr, 1) != 0)
xalloc_die ();
}
@ -652,19 +658,17 @@ static void
time_to_env (char const *envar, struct timespec t)
{
char buf[TIMESPEC_STRSIZE_BOUND];
if (setenv (envar, code_timespec (t, buf), 1) < 0)
if (setenv (envar, code_timespec (t, buf), 1) != 0)
xalloc_die ();
}
static void
oct_to_env (char const *envar, mode_t m)
oct_to_env (char const *envar, unsigned long num)
{
char buf[sizeof "0" + (UINTMAX_WIDTH + 2) / 3];
uintmax_t um = m;
if (EXPR_SIGNED (m) && sizeof m < sizeof um)
um &= ~ (UINTMAX_MAX << TYPE_WIDTH (m));
sprintf (buf, "%#"PRIoMAX, um);
if (setenv (envar, buf, 1) < 0)
char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
snprintf (buf, sizeof buf, "0%lo", num);
if (setenv (envar, buf, 1) != 0)
xalloc_die ();
}
@ -673,7 +677,7 @@ str_to_env (char const *envar, char const *str)
{
if (str)
{
if (setenv (envar, str, 1) < 0)
if (setenv (envar, str, 1) != 0)
xalloc_die ();
}
else
@ -686,7 +690,7 @@ chr_to_env (char const *envar, char c)
char buf[2];
buf[0] = c;
buf[1] = 0;
if (setenv (envar, buf, 1) < 0)
if (setenv (envar, buf, 1) != 0)
xalloc_die ();
}
@ -741,7 +745,7 @@ static pid_t global_pid;
static void (*pipe_handler) (int sig);
int
sys_exec_command (char *file_name, char typechar, struct tar_stat_info *st)
sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
{
int p[2];
@ -774,7 +778,7 @@ sys_wait_command (void)
return;
signal (SIGPIPE, pipe_handler);
while (waitpid (global_pid, &status, 0) < 0)
while (waitpid (global_pid, &status, 0) == -1)
if (errno != EINTR)
{
global_pid = -1;
@ -785,65 +789,53 @@ sys_wait_command (void)
if (WIFEXITED (status))
{
if (!ignore_command_error_option && WEXITSTATUS (status))
paxerror (0, _("%jd: Child returned status %d"),
intmax (global_pid), WEXITSTATUS (status));
ERROR ((0, 0, _("%lu: Child returned status %d"),
(unsigned long) global_pid, WEXITSTATUS (status)));
}
else if (WIFSIGNALED (status))
{
paxwarn (0, _("%jd: Child terminated on signal %d"),
intmax (global_pid), WTERMSIG (status));
WARN ((0, 0, _("%lu: Child terminated on signal %d"),
(unsigned long) global_pid, WTERMSIG (status)));
}
else
paxerror (0, _("%jd: Child terminated on unknown reason"),
intmax (global_pid));
ERROR ((0, 0, _("%lu: Child terminated on unknown reason"),
(unsigned long) global_pid));
global_pid = -1;
}
int
sys_exec_info_script (const char **archive_name, intmax_t volume_number)
sys_exec_info_script (const char **archive_name, int volume_number)
{
pid_t pid;
char uintbuf[UINTMAX_STRSIZE_BOUND];
int p[2];
static void (*saved_handler) (int sig);
xpipe (p);
saved_handler = signal (SIGPIPE, SIG_IGN);
pid_t pid = xfork ();
pid = xfork ();
if (pid != 0)
{
/* Master */
int rc;
int status;
char *buf = NULL;
size_t size = 0;
FILE *fp;
xclose (p[PWRITE]);
FILE *fp = fdopen (p[PREAD], "r");
if (!fp)
{
signal (SIGPIPE, saved_handler);
call_arg_error ("fdopen", info_script_option);
return -1;
}
ssize_t rc = getline (&buf, &size, fp);
if (rc < 0)
{
signal (SIGPIPE, saved_handler);
read_error (info_script_option);
return -1;
}
*archive_name = buf;
buf[rc - 1] = '\0';
if (fclose (fp) < 0)
{
signal (SIGPIPE, saved_handler);
close_error (info_script_option);
return -1;
}
fp = fdopen (p[PREAD], "r");
rc = getline (&buf, &size, fp);
fclose (fp);
while (waitpid (pid, &status, 0) < 0)
if (rc > 0 && buf[rc-1] == '\n')
buf[--rc] = 0;
while (waitpid (pid, &status, 0) == -1)
if (errno != EINTR)
{
signal (SIGPIPE, saved_handler);
@ -852,19 +844,31 @@ sys_exec_info_script (const char **archive_name, intmax_t volume_number)
}
signal (SIGPIPE, saved_handler);
return WIFEXITED (status) ? WEXITSTATUS (status) : -1;
if (WIFEXITED (status))
{
if (WEXITSTATUS (status) == 0 && rc > 0)
*archive_name = buf;
else
free (buf);
return WEXITSTATUS (status);
}
free (buf);
return -1;
}
/* Child */
str_to_env ("TAR_VERSION", PACKAGE_VERSION);
str_to_env ("TAR_ARCHIVE", *archive_name);
dec_to_env ("TAR_VOLUME", volume_number);
dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
setenv ("TAR_ARCHIVE", *archive_name, 1);
setenv ("TAR_VOLUME", STRINGIFY_BIGINT (volume_number, uintbuf), 1);
setenv ("TAR_BLOCKING_FACTOR",
STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
setenv ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT ?
archive_format : current_format), 1);
dec_to_env ("TAR_FD", p[PWRITE]);
setenv ("TAR_FD", STRINGIFY_BIGINT (p[PWRITE], uintbuf), 1);
xclose (p[PREAD]);
@ -875,9 +879,12 @@ sys_exec_info_script (const char **archive_name, intmax_t volume_number)
void
sys_exec_checkpoint_script (const char *script_name,
const char *archive_name,
intmax_t checkpoint_number)
int checkpoint_number)
{
pid_t pid = xfork ();
pid_t pid;
char uintbuf[UINTMAX_STRSIZE_BOUND];
pid = xfork ();
if (pid != 0)
{
@ -885,7 +892,7 @@ sys_exec_checkpoint_script (const char *script_name,
int status;
while (waitpid (pid, &status, 0) < 0)
while (waitpid (pid, &status, 0) == -1)
if (errno != EINTR)
{
waitpid_error (script_name);
@ -896,175 +903,17 @@ sys_exec_checkpoint_script (const char *script_name,
}
/* Child */
str_to_env ("TAR_VERSION", PACKAGE_VERSION);
str_to_env ("TAR_ARCHIVE", archive_name);
dec_to_env ("TAR_CHECKPOINT", checkpoint_number);
dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
str_to_env ("TAR_SUBCOMMAND", subcommand_string (subcommand_option));
str_to_env ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT
? archive_format : current_format));
setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
setenv ("TAR_ARCHIVE", archive_name, 1);
setenv ("TAR_CHECKPOINT", STRINGIFY_BIGINT (checkpoint_number, uintbuf), 1);
setenv ("TAR_BLOCKING_FACTOR",
STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
setenv ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT ?
archive_format : current_format), 1);
priv_set_restore_linkdir ();
xexec (script_name);
}
bool
sys_exec_setmtime_script (const char *script_name,
int dirfd,
const char *file_name,
const char *fmt,
struct timespec *ts)
{
pid_t pid;
int p[2];
bool stop = false;
struct pollfd pfd;
char *buffer = NULL;
idx_t buflen = 0;
idx_t bufsize = 0;
char *cp;
bool rc = true;
if (pipe (p) < 0)
paxfatal (errno, _("pipe failed"));
if ((pid = xfork ()) == 0)
{
char *command = xmalloc (strlen (script_name) + strlen (file_name) + 2);
strcpy (command, script_name);
strcat (command, " ");
strcat (command, file_name);
if (dirfd != AT_FDCWD && fchdir (dirfd) < 0)
paxfatal (errno, _("chdir failed"));
close (p[0]);
if (dup2 (p[1], STDOUT_FILENO) < 0)
paxfatal (errno, _("dup2 failed"));
if (p[1] != STDOUT_FILENO)
close (p[1]);
close (STDIN_FILENO);
if (open (dev_null, O_RDONLY) != STDIN_FILENO)
open_error (dev_null);
priv_set_restore_linkdir ();
/* FIXME: This mishandles shell metacharacters in the file name.
Come to think of it, isn't every use of xexec suspect? */
xexec (command);
}
close (p[1]);
pfd.fd = p[0];
pfd.events = POLLIN;
while (1)
{
int n = poll (&pfd, 1, -1);
if (n < 0)
{
if (errno != EINTR)
{
paxerror (errno, _("poll failed"));
stop = true;
break;
}
}
if (n == 0)
break;
if (pfd.revents & POLLIN)
{
if (buflen == bufsize)
buffer = xpalloc (buffer, &bufsize, 1, -1, 1);
ssize_t nread = read (pfd.fd, buffer + buflen, bufsize - buflen);
if (nread < 0)
{
paxerror (errno, _("error reading output of %s"), script_name);
stop = true;
break;
}
if (nread == 0)
break;
buflen += n;
}
else if (pfd.revents & POLLHUP)
break;
}
close (pfd.fd);
if (stop)
kill (SIGKILL, pid);
sys_wait_for_child (pid, false);
if (stop)
{
free (buffer);
return false;
}
if (buflen == 0)
{
paxerror (0, _("empty output from \"%s %s\""), script_name, file_name);
return false;
}
cp = memchr (buffer, '\n', buflen);
if (cp)
*cp = 0;
else
{
if (buflen == bufsize)
buffer = xirealloc (buffer, ++bufsize);
buffer[buflen] = 0;
}
if (fmt)
{
struct tm tm;
time_t t;
cp = strptime (buffer, fmt, &tm);
if (cp == NULL)
{
paxerror (0, _("output from \"%s %s\" does not satisfy format string:"
" %s"),
script_name, file_name, buffer);
rc = false;
}
else if (*cp != 0)
{
paxwarn (0, _("unconsumed output from \"%s %s\": %s"),
script_name, file_name, cp);
rc = false;
}
else
{
tm.tm_wday = -1;
t = mktime (&tm);
if (tm.tm_wday < 0)
{
paxerror (errno, _("mktime failed"));
rc = false;
}
else
{
ts->tv_sec = t;
ts->tv_nsec = 0;
}
}
}
else if (! parse_datetime (ts, buffer, NULL))
{
paxerror (0, _("unparsable output from \"%s %s\": %s"),
script_name, file_name, buffer);
rc = false;
}
free (buffer);
return rc;
}
#endif /* not MSDOS */

979
src/tar.c

File diff suppressed because it is too large Load Diff

133
src/tar.h
View File

@ -1,6 +1,6 @@
/* GNU tar Archive Format description.
Copyright 1988-2026 Free Software Foundation, Inc.
Copyright 1988-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -48,41 +48,34 @@ struct posix_header
#define TVERSLEN 2
/* Values used in typeflag field. */
enum
{
#define REGTYPE '0' /* regular file */
#define AREGTYPE '\0' /* regular file */
#define LNKTYPE '1' /* link */
#define SYMTYPE '2' /* reserved */
#define CHRTYPE '3' /* character special */
#define BLKTYPE '4' /* block special */
#define DIRTYPE '5' /* directory */
#define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */
REGTYPE = '0', /* regular file */
AREGTYPE = '\0', /* regular file */
LNKTYPE = '1', /* link */
SYMTYPE = '2', /* reserved */
CHRTYPE = '3', /* character special */
BLKTYPE = '4', /* block special */
DIRTYPE = '5', /* directory */
FIFOTYPE = '6', /* FIFO special */
CONTTYPE = '7', /* reserved */
XHDTYPE = 'x', /* Extended header referring to the
#define XHDTYPE 'x' /* Extended header referring to the
next file in the archive */
XGLTYPE = 'g' /* Global extended header */
};
#define XGLTYPE 'g' /* Global extended header */
/* Bits used in the mode field, values in octal. */
enum
{
TSUID = 04000, /* set UID on execution */
TSGID = 02000, /* set GID on execution */
TSVTX = 01000, /* reserved */
#define TSUID 04000 /* set UID on execution */
#define TSGID 02000 /* set GID on execution */
#define TSVTX 01000 /* reserved */
/* file permissions */
TUREAD = 00400, /* read by owner */
TUWRITE = 00200, /* write by owner */
TUEXEC = 00100, /* execute/search by owner */
TGREAD = 00040, /* read by group */
TGWRITE = 00020, /* write by group */
TGEXEC = 00010, /* execute/search by group */
TOREAD = 00004, /* read by other */
TOWRITE = 00002, /* write by other */
TOEXEC = 00001 /* execute/search by other */
};
#define TUREAD 00400 /* read by owner */
#define TUWRITE 00200 /* write by owner */
#define TUEXEC 00100 /* execute/search by owner */
#define TGREAD 00040 /* read by group */
#define TGWRITE 00020 /* write by group */
#define TGEXEC 00010 /* execute/search by group */
#define TOREAD 00004 /* read by other */
#define TOWRITE 00002 /* write by other */
#define TOEXEC 00001 /* execute/search by other */
/* tar Header Block, GNU extensions. */
@ -118,12 +111,9 @@ struct sparse
necessary. The following constants tell how many sparse descriptors fit
in each kind of header able to hold them. */
enum
{
SPARSES_IN_EXTRA_HEADER = 16,
SPARSES_IN_OLDGNU_HEADER = 4,
SPARSES_IN_SPARSE_HEADER = 21
};
#define SPARSES_IN_EXTRA_HEADER 16
#define SPARSES_IN_OLDGNU_HEADER 4
#define SPARSES_IN_SPARSE_HEADER 21
/* Extension header for sparse files, used immediately after the GNU extra
header, and used only if all sparse information cannot fit into that
@ -178,32 +168,29 @@ struct oldgnu_header
'N' Obsolete GNU tar, for file names that do not fit into the main header.
'X' POSIX 1003.1-2001 eXtended (VU version) */
enum
{
/* This is a dir entry that contains the names of files that were in the
dir at the time the dump was made. */
GNUTYPE_DUMPDIR = 'D',
/* This is a dir entry that contains the names of files that were in the
dir at the time the dump was made. */
#define GNUTYPE_DUMPDIR 'D'
/* Identifies the *next* file on the tape as having a long linkname. */
GNUTYPE_LONGLINK = 'K',
/* Identifies the *next* file on the tape as having a long linkname. */
#define GNUTYPE_LONGLINK 'K'
/* Identifies the *next* file on the tape as having a long name. */
GNUTYPE_LONGNAME = 'L',
/* Identifies the *next* file on the tape as having a long name. */
#define GNUTYPE_LONGNAME 'L'
/* This is the continuation of a file that began on another volume. */
GNUTYPE_MULTIVOL = 'M',
/* This is the continuation of a file that began on another volume. */
#define GNUTYPE_MULTIVOL 'M'
/* This is for sparse files. */
GNUTYPE_SPARSE = 'S',
/* This is for sparse files. */
#define GNUTYPE_SPARSE 'S'
/* This file is a tape/volume header. Ignore it on extraction. */
GNUTYPE_VOLHDR = 'V',
/* This file is a tape/volume header. Ignore it on extraction. */
#define GNUTYPE_VOLHDR 'V'
/* Solaris extended header. */
SOLARIS_XHDTYPE = 'X'
};
/* Solaris extended header */
#define SOLARIS_XHDTYPE 'X'
/* Jörg Schilling star header. */
/* J@"org Schilling star header */
struct star_header
{ /* byte offset */
@ -228,11 +215,8 @@ struct star_header
/* 500 */
};
enum
{
SPARSES_IN_STAR_HEADER = 4,
SPARSES_IN_STAR_EXT_HEADER = 21
};
#define SPARSES_IN_STAR_HEADER 4
#define SPARSES_IN_STAR_EXT_HEADER 21
struct star_in_header
{
@ -261,9 +245,8 @@ struct star_ext_header
/* tar Header Block, overall structure. */
/* tar files are made in basic blocks of size BLOCKSIZE.
LG_BLOCKSIZE is the log base 2 of BLOCKSIZE. */
enum { LG_BLOCKSIZE = 9, BLOCKSIZE = 1 << LG_BLOCKSIZE };
/* tar files are made in basic blocks of this size. */
#define BLOCKSIZE 512
enum archive_format
{
@ -288,9 +271,9 @@ struct sp_array
struct xheader
{
struct obstack *stk;
idx_t size;
size_t size;
char *buffer;
idx_t string_length;
uintmax_t string_length;
};
/* Information about xattrs for a file. */
@ -298,14 +281,14 @@ struct xattr_array
{
char *xkey;
char *xval_ptr;
idx_t xval_len;
size_t xval_len;
};
struct xattr_map
{
struct xattr_array *xm_map;
idx_t xm_size; /* Size of the xattr map */
idx_t xm_max; /* Max. number of entries in xattr_map */
size_t xm_size; /* Size of the xattr map */
size_t xm_max; /* Max. number of entries in xattr_map */
};
struct tar_stat_info
@ -323,10 +306,10 @@ struct tar_stat_info
char *cntx_name; /* SELinux context for the current archive entry. */
char *acls_a_ptr; /* Access ACLs for the current archive entry. */
idx_t acls_a_len; /* Access ACLs for the current archive entry. */
size_t acls_a_len; /* Access ACLs for the current archive entry. */
char *acls_d_ptr; /* Default ACLs for the current archive entry. */
idx_t acls_d_len; /* Default ACLs for the current archive entry. */
size_t acls_d_len; /* Default ACLs for the current archive entry. */
struct stat stat; /* regular filesystem stat */
@ -342,12 +325,12 @@ struct tar_stat_info
bool is_sparse; /* Is the file sparse */
/* For sparse files: */
intmax_t sparse_major;
intmax_t sparse_minor;
idx_t sparse_map_avail; /* Index to the first unused element in
unsigned sparse_major;
unsigned sparse_minor;
size_t sparse_map_avail; /* Index to the first unused element in
sparse_map array. Zero if the file is
not sparse */
idx_t sparse_map_size; /* Size of the sparse map */
size_t sparse_map_size; /* Size of the sparse map */
struct sp_array *sparse_map;
off_t real_size; /* The real size of sparse file */

View File

@ -1,5 +1,5 @@
/* This file is part of GNU tar.
Copyright 2006-2026 Free Software Foundation, Inc.
Copyright 2006-2023 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 the
@ -16,8 +16,6 @@
#include <system.h>
#include <regex.h>
#include <mcel.h>
#include <quotearg.h>
#include "common.h"
enum transform_type
@ -51,9 +49,9 @@ struct replace_segm
struct
{
char *ptr;
idx_t size;
size_t size;
} literal; /* type == segm_literal */
idx_t ref; /* type == segm_backref */
size_t ref; /* type == segm_backref */
enum case_ctl_type ctl; /* type == segm_case_ctl */
} v;
};
@ -63,11 +61,11 @@ struct transform
struct transform *next;
enum transform_type transform_type;
int flags;
idx_t match_number;
unsigned match_number;
regex_t regex;
/* Compiled replacement expression */
struct replace_segm *repl_head, *repl_tail;
idx_t segm_count; /* Number of elements in the above list */
size_t segm_count; /* Number of elements in the above list */
};
@ -104,7 +102,7 @@ add_segment (struct transform *tf)
static void
add_literal_segment (struct transform *tf, const char *str, const char *end)
{
idx_t len = end - str;
size_t len = end - str;
if (len)
{
struct replace_segm *segm = add_segment (tf);
@ -117,7 +115,7 @@ add_literal_segment (struct transform *tf, const char *str, const char *end)
}
static void
add_char_segment (struct transform *tf, char chr)
add_char_segment (struct transform *tf, int chr)
{
struct replace_segm *segm = add_segment (tf);
segm->type = segm_literal;
@ -128,15 +126,15 @@ add_char_segment (struct transform *tf, char chr)
}
static void
add_backref_segment (struct transform *tf, idx_t ref)
add_backref_segment (struct transform *tf, size_t ref)
{
struct replace_segm *segm = add_segment (tf);
segm->type = segm_backref;
segm->v.ref = ref;
}
static bool
parse_xform_flags (int *pflags, char c)
static int
parse_xform_flags (int *pflags, int c)
{
switch (c)
{
@ -165,9 +163,9 @@ parse_xform_flags (int *pflags, char c)
break;
default:
return false;
return 1;
}
return true;
return 0;
}
static void
@ -181,7 +179,8 @@ add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl)
static const char *
parse_transform_expr (const char *expr)
{
idx_t i, j;
int delim;
int i, j, rc;
char *str, *beg, *cur;
const char *p;
int cflags = 0;
@ -199,17 +198,18 @@ parse_transform_expr (const char *expr)
expr++;
break;
}
if (!parse_xform_flags (&transform_flags, *expr))
paxusage (_("Unknown transform flag: %c"), *expr);
if (parse_xform_flags (&transform_flags, *expr))
USAGE_ERROR ((0, 0, _("Unknown transform flag: %c"),
*expr));
}
return expr;
}
paxusage (_("Invalid transform expression"));
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
}
char delim = expr[1];
delim = expr[1];
if (!delim)
paxusage (_("Invalid transform expression"));
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
/* Scan regular expression */
for (i = 2; expr[i] && expr[i] != delim; i++)
@ -217,7 +217,7 @@ parse_transform_expr (const char *expr)
i++;
if (expr[i] != delim)
paxusage (_("Invalid transform expression"));
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
/* Scan replacement expression */
for (j = i + 1; expr[j] && expr[j] != delim; j++)
@ -225,7 +225,7 @@ parse_transform_expr (const char *expr)
j++;
if (expr[j] != delim)
paxusage (_("Invalid transform expression"));
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
/* Check flags */
tf->transform_type = transform_first;
@ -247,16 +247,14 @@ parse_transform_expr (const char *expr)
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
char *endp;
tf->match_number = stoint (p, &endp, NULL, 0, IDX_MAX);
p = endp - 1;
}
tf->match_number = strtoul (p, (char**) &p, 0);
p--;
break;
default:
if (!parse_xform_flags (&tf->flags, *p))
paxusage (_("Unknown flag in transform expression: %c"), *p);
if (parse_xform_flags (&tf->flags, *p))
USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
*p));
}
if (*p == ';')
@ -267,13 +265,13 @@ parse_transform_expr (const char *expr)
memcpy (str, expr + 2, i - 2);
str[i - 2] = 0;
int rc = regcomp (&tf->regex, str, cflags);
rc = regcomp (&tf->regex, str, cflags);
if (rc)
{
char errbuf[512];
regerror (rc, &tf->regex, errbuf, sizeof (errbuf));
paxusage (_("Invalid transform expression: %s"), errbuf);
USAGE_ERROR ((0, 0, _("Invalid transform expression: %s"), errbuf));
}
if (str[0] == '^' || (i > 2 && str[i - 3] == '$'))
@ -291,18 +289,17 @@ parse_transform_expr (const char *expr)
{
if (*cur == '\\')
{
size_t n;
add_literal_segment (tf, beg, cur);
switch (*++cur)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
idx_t n = stoint (cur, &cur, NULL, 0, IDX_MAX);
if (tf->regex.re_nsub < n)
paxusage (_("Invalid transform replacement:"
" back reference out of range"));
add_backref_segment (tf, n);
}
n = strtoul (cur, &cur, 10);
if (n > tf->regex.re_nsub)
USAGE_ERROR ((0, 0, _("Invalid transform replacement: back reference out of range")));
add_backref_segment (tf, n);
break;
case '\\':
@ -419,56 +416,74 @@ set_transform_expr (const char *expr)
expr = parse_transform_expr (expr);
}
/* Run case conversion specified by CASE_CTL on array PTR of SIZE
characters. Returns pointer to statically allocated storage. */
static char *
run_case_conv (enum case_ctl_type case_ctl, char *ptr, size_t size)
{
static char *case_ctl_buffer;
static size_t case_ctl_bufsize;
char *p;
if (case_ctl_bufsize < size)
{
case_ctl_bufsize = size;
case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize);
}
memcpy (case_ctl_buffer, ptr, size);
switch (case_ctl)
{
case ctl_upcase_next:
case_ctl_buffer[0] = toupper ((unsigned char) case_ctl_buffer[0]);
break;
case ctl_locase_next:
case_ctl_buffer[0] = tolower ((unsigned char) case_ctl_buffer[0]);
break;
case ctl_upcase:
for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
*p = toupper ((unsigned char) *p);
break;
case ctl_locase:
for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++)
*p = tolower ((unsigned char) *p);
break;
case ctl_stop:
break;
}
return case_ctl_buffer;
}
static struct obstack stk;
static bool stk_init;
/* Run case conversion specified by CASE_CTL on array PTR of SIZE
characters. Append the result to STK. */
static void
run_case_conv (enum case_ctl_type case_ctl, char *ptr, idx_t size)
{
char const *p = ptr, *plim = ptr + size;
mbstate_t mbs; mbszero (&mbs);
while (p < plim)
{
mcel_t g = mcel_scan (p, plim);
char32_t ch;
switch (case_ctl)
{
case ctl_upcase: case ctl_upcase_next: ch = c32toupper (g.ch); break;
case ctl_locase: case ctl_locase_next: ch = c32tolower (g.ch); break;
default: ch = g.ch; break;
}
if (ch == g.ch)
obstack_grow (&stk, p, g.len);
else
{
obstack_make_room (&stk, MB_LEN_MAX);
mbstate_t ombs; mbszero (&ombs);
idx_t outbytes = c32rtomb (obstack_next_free (&stk), ch, &ombs);
obstack_blank_fast (&stk, outbytes);
}
p += g.len;
if (case_ctl != ctl_upcase && case_ctl != ctl_locase)
break;
}
obstack_grow (&stk, p, plim - p);
}
static void
_single_transform_name_to_obstack (struct transform *tf, char *input)
{
regmatch_t *rmp;
int rc;
idx_t nmatches = 0;
size_t nmatches = 0;
enum case_ctl_type case_ctl = ctl_stop, /* Current case conversion op */
save_ctl = ctl_stop; /* Saved case_ctl for \u and \l */
regmatch_t *rmp = xinmalloc (tf->regex.re_nsub + 1, sizeof *rmp);
/* Reset case conversion after a single-char operation */
#define CASE_CTL_RESET() if (case_ctl == ctl_upcase_next \
|| case_ctl == ctl_locase_next) \
{ \
case_ctl = save_ctl; \
save_ctl = ctl_stop; \
}
rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp));
while (*input)
{
idx_t disp;
size_t disp;
char *ptr;
rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
@ -494,28 +509,32 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
switch (segm->type)
{
case segm_literal: /* Literal segment */
run_case_conv (case_ctl,
segm->v.literal.ptr,
segm->v.literal.size);
case_ctl_reset:
/* Reset case conversion after a single-char operation. */
if (case_ctl == ctl_upcase_next
|| case_ctl == ctl_locase_next)
if (case_ctl == ctl_stop)
ptr = segm->v.literal.ptr;
else
{
case_ctl = save_ctl;
save_ctl = ctl_stop;
ptr = run_case_conv (case_ctl,
segm->v.literal.ptr,
segm->v.literal.size);
CASE_CTL_RESET();
}
obstack_grow (&stk, ptr, segm->v.literal.size);
break;
case segm_backref: /* Back-reference segment */
if (0 <= rmp[segm->v.ref].rm_so
&& 0 <= rmp[segm->v.ref].rm_eo)
if (rmp[segm->v.ref].rm_so != -1
&& rmp[segm->v.ref].rm_eo != -1)
{
idx_t size = (rmp[segm->v.ref].rm_eo
- rmp[segm->v.ref].rm_so);
run_case_conv (case_ctl,
input + rmp[segm->v.ref].rm_so, size);
goto case_ctl_reset;
size_t size = rmp[segm->v.ref].rm_eo
- rmp[segm->v.ref].rm_so;
ptr = input + rmp[segm->v.ref].rm_so;
if (case_ctl != ctl_stop)
{
ptr = run_case_conv (case_ctl, ptr, size);
CASE_CTL_RESET();
}
obstack_grow (&stk, ptr, size);
}
break;
@ -562,11 +581,11 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
free (rmp);
}
static void
static bool
_transform_name_to_obstack (int flags, char *input, char **output)
{
struct transform *tf;
bool ok = false;
bool alloced = false;
if (!stk_init)
{
@ -580,53 +599,38 @@ _transform_name_to_obstack (int flags, char *input, char **output)
{
_single_transform_name_to_obstack (tf, input);
input = obstack_finish (&stk);
ok = true;
alloced = true;
}
}
if (!ok)
{
obstack_grow0 (&stk, input, strlen (input));
input = obstack_finish (&stk);
}
*output = input;
return alloced;
}
/* Transform name *PINPUT of a file or archive member of type TYPE
(a single XFORM_* bit). If FUN is not NULL, call this function
to further transform the result. Arguments to FUN are the transformed
name and type, it's return value is the new transformed name.
If transformation results in a non-empty string, store the result in
*PINPUT and return true. Otherwise, if it results in an empty string,
issue a warning, return false and don't modify PINPUT.
*/
bool
transform_name_fp (char **pinput, int type,
char const *(*fun) (char const *, int))
transform_name_fp (char **pinput, int flags,
char *(*fun)(char *, void *), void *dat)
{
char *str;
char const *result;
_transform_name_to_obstack (type, *pinput, &str);
result = (str[0] != 0 && fun) ? fun (str, type) : str;
if (result[0] == 0)
{
warnopt (WARN_EMPTY_TRANSFORM, 0,
_("%s: transforms to empty name"), quotearg_colon (*pinput));
obstack_free (&stk, str);
return false;
}
assign_string (pinput, result);
obstack_free (&stk, str);
return true;
char *str;
bool ret = _transform_name_to_obstack (flags, *pinput, &str);
if (ret)
{
assign_string (pinput, fun ? fun (str, dat) : str);
obstack_free (&stk, str);
}
else if (fun)
{
*pinput = NULL;
assign_string (pinput, fun (str, dat));
free (str);
ret = true;
}
return ret;
}
bool
transform_name (char **pinput, int type)
{
return transform_name_fp (pinput, type, NULL);
return transform_name_fp (pinput, type, NULL, NULL);
}
bool

View File

@ -1,6 +1,6 @@
/* Unlink files.
Copyright 2009-2026 Free Software Foundation, Inc.
Copyright 2009-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -24,7 +24,7 @@
struct deferred_unlink
{
struct deferred_unlink *next; /* Next unlink in the queue */
idx_t dir_idx; /* Directory index in wd */
int dir_idx; /* Directory index in wd */
char *file_name; /* Name of the file to unlink, relative
to dir_idx */
bool is_dir; /* True if file_name is a directory */
@ -32,25 +32,35 @@ struct deferred_unlink
entry got added to the queue */
};
static bool
is_cwd (struct deferred_unlink const *p)
{
return p->is_dir && !p->file_name[p->file_name[0] == '.'];
}
#define IS_CWD(p) \
((p)->is_dir \
&& ((p)->file_name[0] == 0 || strcmp ((p)->file_name, ".") == 0))
/* The unlink queue */
static struct deferred_unlink *dunlink_head, *dunlink_tail;
/* Number of entries in the queue */
static size_t dunlink_count;
/* List of entries available for allocation */
static struct deferred_unlink *dunlink_avail;
/* Delay (number of records written) between adding entry to the
list and its actual removal. */
static size_t deferred_unlink_delay = 0;
static struct deferred_unlink *
dunlink_alloc (void)
{
struct deferred_unlink *p = dunlink_avail;
if (!p)
return xmalloc (sizeof *p);
dunlink_avail = p->next;
struct deferred_unlink *p;
if (dunlink_avail)
{
p = dunlink_avail;
dunlink_avail = p->next;
p->next = NULL;
}
else
p = xmalloc (sizeof (*p));
return p;
}
@ -62,13 +72,14 @@ dunlink_insert (struct deferred_unlink *anchor, struct deferred_unlink *p)
p->next = anchor->next;
anchor->next = p;
}
else
else
{
p->next = dunlink_head;
dunlink_head = p;
}
if (!p->next)
dunlink_tail = p;
dunlink_count++;
}
static void
@ -83,21 +94,21 @@ static void
flush_deferred_unlinks (bool force)
{
struct deferred_unlink *p, *prev = NULL;
idx_t saved_chdir = chdir_current;
int saved_chdir = chdir_current;
for (p = dunlink_head; p; )
{
struct deferred_unlink *next = p->next;
if (force
|| p->records_written < records_written)
|| records_written > p->records_written + deferred_unlink_delay)
{
chdir_do (p->dir_idx);
if (p->is_dir)
{
const char *fname;
if (p->dir_idx && is_cwd (p))
if (p->dir_idx && IS_CWD (p))
{
prev = p;
p = next;
@ -106,10 +117,7 @@ flush_deferred_unlinks (bool force)
else
fname = p->file_name;
struct fdbase f = fdbase (fname);
if (f.fd != BADFD && unlinkat (f.fd, f.base, AT_REMOVEDIR) == 0)
fdbase_clear ();
else
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
{
switch (errno)
{
@ -135,13 +143,11 @@ flush_deferred_unlinks (bool force)
}
else
{
struct fdbase f = fdbase (p->file_name);
if (f.fd != BADFD && unlinkat (f.fd, f.base, 0) == 0)
fdbase_clear ();
else if (errno != ENOENT)
if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
unlink_error (p->file_name);
}
dunlink_reclaim (p);
dunlink_count--;
p = next;
if (prev)
prev->next = p;
@ -164,7 +170,7 @@ flush_deferred_unlinks (bool force)
const char *fname;
chdir_do (p->dir_idx);
if (p->dir_idx && is_cwd (p))
if (p->dir_idx && IS_CWD (p))
{
fname = tar_dirname ();
chdir_do (p->dir_idx - 1);
@ -172,18 +178,18 @@ flush_deferred_unlinks (bool force)
else
fname = p->file_name;
struct fdbase f = fdbase (fname);
if (f.fd != BADFD && unlinkat (f.fd, f.base, AT_REMOVEDIR) == 0)
fdbase_clear ();
else if (errno != ENOENT)
rmdir_error (fname);
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
{
if (errno != ENOENT)
rmdir_error (fname);
}
dunlink_reclaim (p);
dunlink_count--;
p = next;
}
dunlink_head = dunlink_tail = NULL;
}
}
chdir_do (saved_chdir);
}
@ -191,7 +197,7 @@ void
finish_deferred_unlinks (void)
{
flush_deferred_unlinks (true);
while (dunlink_avail)
{
struct deferred_unlink *next = dunlink_avail->next;
@ -206,7 +212,7 @@ queue_deferred_unlink (const char *name, bool is_dir)
struct deferred_unlink *p;
if (dunlink_head
&& records_written > dunlink_head->records_written)
&& records_written > dunlink_head->records_written + deferred_unlink_delay)
flush_deferred_unlinks (false);
p = dunlink_alloc ();
@ -217,11 +223,11 @@ queue_deferred_unlink (const char *name, bool is_dir)
p->is_dir = is_dir;
p->records_written = records_written;
if (is_cwd (p))
if (IS_CWD (p))
{
struct deferred_unlink *q, *prev;
for (q = dunlink_head, prev = NULL; q; prev = q, q = q->next)
if (is_cwd (q) && q->dir_idx < p->dir_idx)
if (IS_CWD (q) && q->dir_idx < p->dir_idx)
break;
if (q)
dunlink_insert (prev, p);

View File

@ -1,6 +1,6 @@
/* Update a tar archive.
Copyright 1988-2026 Free Software Foundation, Inc.
Copyright 1988-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -26,6 +26,10 @@
#include <quotearg.h>
#include "common.h"
/* FIXME: This module should not directly handle the following variable,
instead, this should be done in buffer.c only. */
extern union block *current_block;
/* We've hit the end of the old stuff, and its time to start writing new
stuff to the tape. This involves seeking back one record and
re-writing the current record (which has been changed).
@ -45,8 +49,7 @@ static bool acting_as_filter;
static void
append_file (char *file_name)
{
struct fdbase f = fdbase (file_name);
int handle = f.fd == BADFD ? -1 : openat (f.fd, f.base, O_RDONLY | O_BINARY);
int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
if (handle < 0)
{
@ -57,19 +60,23 @@ append_file (char *file_name)
while (true)
{
union block *start = find_next_block ();
idx_t bufsize = available_space_after (start);
idx_t status = full_read (handle, charptr (start), bufsize);
if (status < bufsize && errno)
read_fatal (file_name);
size_t status = full_read (handle, start->buffer,
available_space_after (start));
if (status == 0)
break;
idx_t rem = status % BLOCKSIZE;
if (rem)
memset (charptr (start) + (status - rem), 0, BLOCKSIZE - rem);
set_next_block_after (charptr (start) + status - 1);
{
if (errno == 0)
break;
read_fatal (file_name);
}
if (status == SAFE_READ_ERROR)
read_fatal (file_name);
if (status % BLOCKSIZE)
memset (start->buffer + status - status % BLOCKSIZE, 0,
BLOCKSIZE - status % BLOCKSIZE);
set_next_block_after (start + (status - 1) / BLOCKSIZE);
}
if (close (handle) < 0)
if (close (handle) != 0)
close_error (file_name);
}
@ -103,7 +110,7 @@ update_archive (void)
name_gather ();
open_archive (ACCESS_UPDATE);
acting_as_filter = streq (archive_name_array[0], "-");
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
xheader_forbid_global ();
while (!found_end)
@ -123,7 +130,7 @@ update_archive (void)
struct name *name;
decode_header (current_header, &current_stat_info,
&current_format, false);
&current_format, 0);
transform_stat_info (current_header->header.typeflag,
&current_stat_info);
archive_format = current_format;
@ -138,9 +145,7 @@ update_archive (void)
{
if (S_ISDIR (s.st_mode))
{
char *p;
char *dirp = tar_savedir (current_stat_info.file_name,
true);
char *p, *dirp = tar_savedir (current_stat_info.file_name, 1);
if (dirp)
{
namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
@ -185,11 +190,11 @@ update_archive (void)
switch (previous_status)
{
case HEADER_STILL_UNREAD:
paxwarn (0, _("This does not look like a tar archive"));
WARN ((0, 0, _("This does not look like a tar archive")));
FALLTHROUGH;
case HEADER_SUCCESS:
case HEADER_ZERO_BLOCK:
paxerror (0, _("Skipping to next header"));
ERROR ((0, 0, _("Skipping to next header")));
FALLTHROUGH;
case HEADER_FAILURE:
break;
@ -207,7 +212,7 @@ update_archive (void)
reset_eof ();
time_to_start_writing = true;
output_start = charptr (current_block);
output_start = current_block->buffer;
{
struct name const *p;
@ -221,7 +226,7 @@ update_archive (void)
if (subcommand_option == CAT_SUBCOMMAND)
append_file (file_name);
else
dump_file (NULL, file_name, file_name);
dump_file (0, file_name, file_name);
}
}

View File

@ -1,6 +1,6 @@
/* Charset handling for GNU tar.
Copyright 2004-2026 Free Software Foundation, Inc.
Copyright 2004-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -35,8 +35,7 @@
# define iconv_open(tocode, fromcode) ((iconv_t) -1)
# undef iconv
# define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) \
(errno = ENOSYS, SIZE_MAX)
# define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) (errno = ENOSYS, (size_t) -1)
# undef iconv_close
# define iconv_close(cd) 0
@ -54,14 +53,14 @@ static iconv_t conv_desc[2] = { (iconv_t) -1, (iconv_t) -1 };
static iconv_t
utf8_init (bool to_utf)
{
if (conv_desc[to_utf] == (iconv_t) -1)
if (conv_desc[(int) to_utf] == (iconv_t) -1)
{
if (to_utf)
conv_desc[to_utf] = iconv_open ("UTF-8", locale_charset ());
conv_desc[(int) to_utf] = iconv_open ("UTF-8", locale_charset ());
else
conv_desc[to_utf] = iconv_open (locale_charset (), "UTF-8");
conv_desc[(int) to_utf] = iconv_open (locale_charset (), "UTF-8");
}
return conv_desc[to_utf];
return conv_desc[(int) to_utf];
}
bool
@ -73,19 +72,16 @@ utf8_convert (bool to_utf, char const *input, char **output)
size_t outlen;
iconv_t cd = utf8_init (to_utf);
if (!cd)
if (cd == 0)
{
*output = xstrdup (input);
return true;
}
else if (cd == (iconv_t) -1)
else if (cd == (iconv_t)-1)
return false;
inlen = strlen (input) + 1;
bool overflow = ckd_mul (&outlen, inlen, MB_LEN_MAX);
overflow |= ckd_add (&outlen, outlen, 1);
if (overflow)
xalloc_die ();
outlen = inlen * MB_LEN_MAX + 1;
ob = ret = xmalloc (outlen);
ib = (char ICONV_CONST *) input;
/* According to POSIX, "if iconv() encounters a character in the input
@ -94,7 +90,7 @@ utf8_convert (bool to_utf, char const *input, char **output)
implementation-defined conversion on this character." It will "update
the variables pointed to by the arguments to reflect the extent of the
conversion and return the number of non-identical conversions performed".
On error, it returns SIZE_MAX.
On error, it returns -1.
In other words, non-zero return always indicates failure, either because
the input was not fully converted, or because it was converted in a
non-reversible way.

View File

@ -1,6 +1,6 @@
/* Warnings for GNU tar.
Copyright 2009-2026 Free Software Foundation, Inc.
Copyright 2009-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -50,7 +50,6 @@ static char const *const warning_args[] = {
"failed-read",
"missing-zero-blocks",
"verbose",
"empty-transform",
NULL
};
@ -82,7 +81,6 @@ static int warning_types[] = {
WARN_FAILED_READ,
WARN_MISSING_ZERO_BLOCKS,
WARN_VERBOSE_WARNINGS,
WARN_EMPTY_TRANSFORM
};
ARGMATCH_VERIFY (warning_args, warning_types);
@ -92,17 +90,17 @@ int warning_option = WARN_ALL & ~(WARN_VERBOSE_WARNINGS|WARN_MISSING_ZERO_BLOCKS
void
set_warning_option (const char *arg)
{
bool negate = false;
int negate = 0;
int option;
if (streq (arg, "none"))
if (strcmp (arg, "none") == 0)
{
warning_option = 0;
return;
}
if (strncmp (arg, "no-", 3) == 0)
if (strlen (arg) > 2 && memcmp (arg, "no-", 3) == 0)
{
negate = true;
negate = 1;
arg += 3;
}
@ -113,17 +111,3 @@ set_warning_option (const char *arg)
else
warning_option |= option;
}
void
warnopt (int opt, int errnum, char const *format, ...)
{
if (warning_enabled (opt))
{
if (error_hook)
error_hook ();
va_list ap;
va_start (ap, format);
verror (0, errnum, format, ap);
va_end (ap);
}
}

View File

@ -1,6 +1,6 @@
/* Support for extended attributes.
Copyright (C) 2006-2026 Free Software Foundation, Inc.
Copyright (C) 2006-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -30,8 +30,8 @@
#include "xattr-at.h"
#include "selinux-at.h"
static char const XATTRS_PREFIX[] = "SCHILY.xattr.";
enum { XATTRS_PREFIX_LEN = sizeof XATTRS_PREFIX - 1 };
#define XATTRS_PREFIX "SCHILY.xattr."
#define XATTRS_PREFIX_LEN (sizeof XATTRS_PREFIX - 1)
void
xheader_xattr_init (struct tar_stat_info *st)
@ -54,7 +54,9 @@ xattr_map_init (struct xattr_map *map)
void
xattr_map_free (struct xattr_map *xattr_map)
{
for (idx_t i = 0; i < xattr_map->xm_size; i++)
size_t i;
for (i = 0; i < xattr_map->xm_size; i++)
{
free (xattr_map->xm_map[i].xkey);
free (xattr_map->xm_map[i].xval_ptr);
@ -64,23 +66,25 @@ xattr_map_free (struct xattr_map *xattr_map)
void
xattr_map_add (struct xattr_map *map,
const char *key, const char *val, idx_t len)
const char *key, const char *val, size_t len)
{
struct xattr_array *p;
if (map->xm_size == map->xm_max)
map->xm_map = xpalloc (map->xm_map, &map->xm_max, 1, -1,
sizeof *map->xm_map);
struct xattr_array *p = &map->xm_map[map->xm_size];
map->xm_map = x2nrealloc (map->xm_map, &map->xm_max,
sizeof (map->xm_map[0]));
p = &map->xm_map[map->xm_size];
p->xkey = xstrdup (key);
p->xval_ptr = ximemdup (val, len + 1);
p->xval_ptr = xmemdup (val, len + 1);
p->xval_len = len;
map->xm_size++;
}
MAYBE_UNUSED static void
void
xheader_xattr_add (struct tar_stat_info *st,
const char *key, const char *val, idx_t len)
const char *key, const char *val, size_t len)
{
idx_t klen = strlen (key);
size_t klen = strlen (key);
char *xkey = xmalloc (XATTRS_PREFIX_LEN + klen + 1);
char *tmp = xkey;
@ -95,7 +99,9 @@ xheader_xattr_add (struct tar_stat_info *st,
void
xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
{
for (idx_t i = 0; i < src->xm_size; i++)
size_t i;
for (i = 0; i < src->xm_size; i++)
xattr_map_add (dst, src->xm_map[i].xkey,
src->xm_map[i].xval_ptr,
src->xm_map[i].xval_len);
@ -104,8 +110,8 @@ xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
struct xattrs_mask_map
{
const char **masks;
idx_t size;
idx_t used;
size_t size;
size_t used;
};
/* list of fnmatch patterns */
@ -254,7 +260,7 @@ fixup_extra_acl_fields (char *ptr)
while (*src)
{
const char *old = src;
idx_t len = 0;
size_t len = 0;
src = skip_to_ext_fields (src);
len = src - old;
@ -278,7 +284,7 @@ fixup_extra_acl_fields (char *ptr)
attribute. Called only when acls_option > 0. */
static void
xattrs__acls_set (struct tar_stat_info const *st,
char const *file_name, acl_type_t type,
char const *file_name, int type,
char *ptr, bool def)
{
acl_t acl;
@ -293,12 +299,12 @@ xattrs__acls_set (struct tar_stat_info const *st,
/* No "default" IEEE 1003.1e ACL set for directory. At this moment,
FILE_NAME may already have inherited default acls from parent
directory; clean them up. */
struct fdbase f1 = fdbase (file_name);
if (f1.fd == BADFD || acl_delete_def_file_at (f1.fd, f1.base) < 0)
warnopt (WARN_XATTR_WRITE, errno,
if (acl_delete_def_file_at (chdir_fd, file_name))
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_("acl_delete_def_file_at: Cannot drop default POSIX ACLs "
"for file '%s'"),
quote (file_name));
file_name));
return;
}
else
@ -310,12 +316,12 @@ xattrs__acls_set (struct tar_stat_info const *st,
return;
}
struct fdbase f = fdbase (file_name);
if (f.fd == BADFD || acl_set_file_at (f.fd, f.base, type, acl) < 0)
if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
/* warn even if filesystem does not support acls */
warnopt (WARN_XATTR_WRITE, errno,
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
quote (file_name));
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
file_name));
acl_free (acl);
}
@ -323,7 +329,7 @@ xattrs__acls_set (struct tar_stat_info const *st,
/* Cleanup textual representation of the ACL in VAL by eliminating tab
characters and comments */
static void
xattrs_acls_cleanup (char *val, idx_t *plen)
xattrs_acls_cleanup (char *val, size_t *plen)
{
char *p, *q;
@ -346,7 +352,7 @@ xattrs_acls_cleanup (char *val, idx_t *plen)
static void
acls_get_text (int parentfd, const char *file_name, acl_type_t type,
char **ret_ptr, idx_t *ret_len)
char **ret_ptr, size_t * ret_len)
{
char *val = NULL;
acl_t acl;
@ -364,12 +370,11 @@ acls_get_text (int parentfd, const char *file_name, acl_type_t type,
val = acl_to_any_text (acl, NULL, '\n',
TEXT_SOME_EFFECTIVE | TEXT_NUMERIC_IDS);
#else
static bool warned;
static int warned;
if (!warned)
{
warned = true;
paxwarn (0, _("--numeric-owner is ignored for ACLs:"
" libacl is not available"));
WARN ((0, 0, _("--numeric-owner is ignored for ACLs: libacl is not available")));
warned = 1;
}
#endif
}
@ -390,7 +395,7 @@ acls_get_text (int parentfd, const char *file_name, acl_type_t type,
static void
xattrs__acls_get_a (int parentfd, const char *file_name,
char **ret_ptr, idx_t *ret_len)
char **ret_ptr, size_t *ret_len)
{
acls_get_text (parentfd, file_name, ACL_TYPE_ACCESS, ret_ptr, ret_len);
}
@ -398,7 +403,7 @@ xattrs__acls_get_a (int parentfd, const char *file_name,
/* "system.posix_acl_default" */
static void
xattrs__acls_get_d (int parentfd, char const *file_name,
char **ret_ptr, idx_t *ret_len)
char **ret_ptr, size_t * ret_len)
{
acls_get_text (parentfd, file_name, ACL_TYPE_DEFAULT, ret_ptr, ret_len);
}
@ -406,13 +411,13 @@ xattrs__acls_get_d (int parentfd, char const *file_name,
static void
acls_one_line (const char *prefix, char delim,
const char *aclstring, idx_t len)
const char *aclstring, size_t len)
{
/* support both long and short text representation of posix acls */
struct obstack stk;
idx_t pref_len = strlen (prefix);
int pref_len = strlen (prefix);
const char *oldstring = aclstring;
idx_t pos = 0;
int pos = 0;
if (!aclstring || !len)
return;
@ -420,7 +425,7 @@ acls_one_line (const char *prefix, char delim,
obstack_init (&stk);
while (pos <= len)
{
idx_t move = strcspn (aclstring, ",\n");
int move = strcspn (aclstring, ",\n");
if (!move)
break;
@ -436,30 +441,27 @@ acls_one_line (const char *prefix, char delim,
obstack_1grow (&stk, '\0');
fputs (obstack_finish (&stk), stdlis);
fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
obstack_free (&stk, NULL);
}
void
xattrs_acls_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED struct tar_stat_info *st,
MAYBE_UNUSED bool xisfile)
xattrs_acls_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int xisfile)
{
if (acls_option > 0)
{
#ifndef HAVE_POSIX_ACLS
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("POSIX ACL support is not available"));
}
WARN ((0, 0, _("POSIX ACL support is not available")));
done = 1;
#else
int err = file_has_acl_at (parentfd, file_name, &st->stat);
if (err == 0)
return;
if (err < 0)
if (err == -1)
{
call_arg_warn ("file_has_acl_at", file_name);
return;
@ -475,18 +477,16 @@ xattrs_acls_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
}
void
xattrs_acls_set (MAYBE_UNUSED struct tar_stat_info const *st,
MAYBE_UNUSED char const *file_name, char typeflag)
xattrs_acls_set (struct tar_stat_info const *st,
char const *file_name, char typeflag)
{
if (acls_option > 0 && typeflag != SYMTYPE)
{
#ifndef HAVE_POSIX_ACLS
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("POSIX ACL support is not available"));
}
WARN ((0, 0, _("POSIX ACL support is not available")));
done = 1;
#else
xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
st->acls_a_ptr, false);
@ -501,7 +501,11 @@ static void
mask_map_realloc (struct xattrs_mask_map *map)
{
if (map->used == map->size)
map->masks = xpalloc (map->masks, &map->size, 1, -1, sizeof *map->masks);
{
if (map->size == 0)
map->size = 4;
map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
}
}
void
@ -516,6 +520,20 @@ xattrs_mask_add (const char *mask, bool incl)
mask_map->masks[mask_map->used++] = mask;
}
static void
clear_mask_map (struct xattrs_mask_map *mask_map)
{
if (mask_map->size)
free (mask_map->masks);
}
void
xattrs_clear_setup (void)
{
clear_mask_map (&xattrs_setup.incl);
clear_mask_map (&xattrs_setup.excl);
}
static bool xattrs_masked_out (const char *kw, bool archiving);
/* get xattrs from file given by FILE_NAME or FD (when non-zero)
@ -523,59 +541,59 @@ static bool xattrs_masked_out (const char *kw, bool archiving);
if no mask is given this includes all the user.*, security.*, system.*,
etc. available domains */
void
xattrs_xattrs_get (MAYBE_UNUSED int parentfd,
MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED struct tar_stat_info *st, MAYBE_UNUSED int fd)
xattrs_xattrs_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd)
{
if (xattrs_option)
if (xattrs_option > 0)
{
#ifndef HAVE_XATTRS
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("XATTR support is not available"));
}
WARN ((0, 0, _("XATTR support is not available")));
done = 1;
#else
static idx_t xsz = 1024 / 2 * 3;
static size_t xsz = 1024;
static char *xatrs = NULL;
ssize_t xret;
ssize_t xret = -1;
while (!xatrs
|| (((xret = (fd == 0
? listxattrat (parentfd, file_name, xatrs, xsz)
: flistxattr (fd, xatrs, xsz)))
< 0)
&& errno == ERANGE))
if (!xatrs)
xatrs = x2nrealloc (xatrs, &xsz, 1);
while (((fd == 0) ?
((xret =
llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
((xret = flistxattr (fd, xatrs, xsz)) == -1))
&& (errno == ERANGE))
{
xatrs = xpalloc (xatrs, &xsz, 1, -1, sizeof *xatrs);
xatrs = x2nrealloc (xatrs, &xsz, 1);
}
if (xret < 0)
if (xret == -1)
call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
else
{
const char *attr = xatrs;
static idx_t asz = 1024 / 2 * 3;
static size_t asz = 1024;
static char *val = NULL;
if (!val)
val = x2nrealloc (val, &asz, 1);
while (xret > 0)
{
idx_t len = strlen (attr);
size_t len = strlen (attr);
ssize_t aret = 0;
while (!val
|| (((aret = (fd == 0
? lgetxattrat (parentfd, file_name, attr,
val, asz)
: fgetxattr (fd, attr, val, asz)))
< 0)
&& errno == ERANGE))
while (((fd == 0)
? ((aret = lgetxattrat (parentfd, file_name, attr,
val, asz)) == -1)
: ((aret = fgetxattr (fd, attr, val, asz)) == -1))
&& (errno == ERANGE))
{
val = xpalloc (val, &asz, 1, -1, sizeof *val);
val = x2nrealloc (val, &asz, 1);
}
if (0 <= aret)
if (aret != -1)
{
if (!xattrs_masked_out (attr, true))
xheader_xattr_add (st, attr, val, aret);
@ -595,28 +613,26 @@ xattrs_xattrs_get (MAYBE_UNUSED int parentfd,
#ifdef HAVE_XATTRS
static void
xattrs__fd_set (char const *file_name, char typeflag,
const char *attr, const char *ptr, idx_t len)
const char *attr, const char *ptr, size_t len)
{
if (ptr)
{
const char *sysname = "setxattrat";
int ret;
struct fdbase f = fdbase (file_name);
int ret = -1;
if (f.fd == BADFD)
ret = -1;
else if (typeflag != SYMTYPE)
ret = setxattrat (f.fd, f.base, attr, ptr, len, 0);
if (typeflag != SYMTYPE)
ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
else
{
sysname = "lsetxattr";
ret = lsetxattrat (f.fd, f.base, attr, ptr, len, 0);
ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
}
if (ret < 0)
warnopt (WARN_XATTR_WRITE, errno,
_("%s: Cannot set '%s' extended attribute for file '%s'"),
sysname, attr, quote (file_name));
if (ret == -1)
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_("%s: Cannot set '%s' extended attribute for file '%s'"),
sysname, attr, file_name));
}
}
#endif
@ -624,42 +640,38 @@ xattrs__fd_set (char const *file_name, char typeflag,
/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
zero, otherwise the fgetfileconat is used against correct file descriptor */
void
xattrs_selinux_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED struct tar_stat_info *st, MAYBE_UNUSED int fd)
xattrs_selinux_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("SELinux support is not available"));
}
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
#else
int result = (fd
? fgetfilecon (fd, &st->cntx_name)
: lgetfileconat (parentfd, file_name, &st->cntx_name));
int result = fd ?
fgetfilecon (fd, &st->cntx_name)
: lgetfileconat (parentfd, file_name, &st->cntx_name);
if (result < 0 && errno != ENODATA && errno != ENOTSUP)
if (result == -1 && errno != ENODATA && errno != ENOTSUP)
call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
#endif
}
}
void
xattrs_selinux_set (MAYBE_UNUSED struct tar_stat_info const *st,
MAYBE_UNUSED char const *file_name, MAYBE_UNUSED char typeflag)
xattrs_selinux_set (struct tar_stat_info const *st,
char const *file_name, char typeflag)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("SELinux support is not available"));
}
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
#else
const char *sysname = "setfilecon";
int ret;
@ -667,24 +679,22 @@ xattrs_selinux_set (MAYBE_UNUSED struct tar_stat_info const *st,
if (!st->cntx_name)
return;
struct fdbase f = fdbase (file_name);
if (f.fd == BADFD)
ret = -1;
else if (typeflag != SYMTYPE)
if (typeflag != SYMTYPE)
{
ret = setfileconat (f.fd, f.base, st->cntx_name);
ret = setfileconat (chdir_fd, file_name, st->cntx_name);
sysname = "setfileconat";
}
else
{
ret = lsetfileconat (f.fd, f.base, st->cntx_name);
ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
sysname = "lsetfileconat";
}
if (ret < 0)
warnopt (WARN_XATTR_WRITE, errno,
_("%s: Cannot set SELinux context for file '%s'"),
sysname, quote (file_name));
if (ret == -1)
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_("%s: Cannot set SELinux context for file '%s'"),
sysname, file_name));
#endif
}
}
@ -692,20 +702,23 @@ xattrs_selinux_set (MAYBE_UNUSED struct tar_stat_info const *st,
static bool
xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
{
int i;
if (!mm->size)
return false;
for (idx_t i = 0; i < mm->used; i++)
for (i = 0; i < mm->used; i++)
if (fnmatch (mm->masks[i], kw, 0) == 0)
return true;
return false;
}
#define USER_DOT_PFX "user."
static bool
xattrs_kw_included (const char *kw, bool archiving)
{
static char const USER_DOT_PFX[] = "user.";
if (xattrs_setup.incl.size)
return xattrs_matches_mask (kw, &xattrs_setup.incl);
else if (archiving)
@ -731,24 +744,23 @@ xattrs_masked_out (const char *kw, bool archiving)
}
void
xattrs_xattrs_set (MAYBE_UNUSED struct tar_stat_info const *st,
MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED char typeflag, MAYBE_UNUSED bool later_run)
xattrs_xattrs_set (struct tar_stat_info const *st,
char const *file_name, char typeflag, int later_run)
{
if (xattrs_option)
if (xattrs_option > 0)
{
#ifndef HAVE_XATTRS
static bool done;
static int done = 0;
if (!done)
{
done = true;
paxwarn (0, _("XATTR support is not available"));
}
WARN ((0, 0, _("XATTR support is not available")));
done = 1;
#else
size_t i;
if (!st->xattr_map.xm_size)
return;
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
for (i = 0; i < st->xattr_map.xm_size; i++)
{
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
@ -759,7 +771,7 @@ xattrs_xattrs_set (MAYBE_UNUSED struct tar_stat_info const *st,
the first run except 'security.capability' which is restored in
'later_run == 1'. */
if (typeflag == REGTYPE
&& streq (keyword, "security.capability") != later_run)
&& later_run == !!strcmp (keyword, "security.capability"))
continue;
if (xattrs_masked_out (keyword, false /* extracting */ ))
@ -777,21 +789,23 @@ xattrs_xattrs_set (MAYBE_UNUSED struct tar_stat_info const *st,
void
xattrs_print_char (struct tar_stat_info const *st, char *output)
{
int i;
if (verbose_option < 2)
{
*output = 0;
return;
}
if (xattrs_option || selinux_context_option > 0 || acls_option > 0)
if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
{
/* placeholders */
*output = ' ';
output[1] = 0;
}
if (xattrs_option && st->xattr_map.xm_size)
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
if (xattrs_option > 0 && st->xattr_map.xm_size)
for (i = 0; i < st->xattr_map.xm_size; ++i)
{
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
if (!xattrs_masked_out (keyword, false /* like extracting */ ))
@ -830,14 +844,16 @@ xattrs_print (struct tar_stat_info const *st)
}
/* xattrs */
if (xattrs_option && st->xattr_map.xm_size)
if (xattrs_option > 0 && st->xattr_map.xm_size)
{
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
int i;
for (i = 0; i < st->xattr_map.xm_size; ++i)
{
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
if (!xattrs_masked_out (keyword, false /* like extracting */ ))
fprintf (stdlis, " x: %td %s\n",
st->xattr_map.xm_map[i].xval_len, keyword);
fprintf (stdlis, " x: %lu %s\n",
(unsigned long) st->xattr_map.xm_map[i].xval_len, keyword);
}
}
}

View File

@ -1,6 +1,6 @@
/* Support for extended attributes.
Copyright (C) 2006-2026 Free Software Foundation, Inc.
Copyright (C) 2006-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -26,8 +26,11 @@
to true/false if you want to add include/exclude pattern */
extern void xattrs_mask_add (const char *mask, bool incl);
/* clear helping structures when tar finishes */
extern void xattrs_clear_setup (void);
extern void xattrs_acls_get (int parentfd, char const *file_name,
struct tar_stat_info *st, bool xisfile);
struct tar_stat_info *st, int xisfile);
extern void xattrs_selinux_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd);
extern void xattrs_xattrs_get (int parentfd, char const *file_name,
@ -39,7 +42,7 @@ extern void xattrs_selinux_set (struct tar_stat_info const *st,
char const *file_name, char typeflag);
extern void xattrs_xattrs_set (struct tar_stat_info const *st,
char const *file_name, char typeflag,
bool later_run);
int later_run);
extern void xattrs_print_char (struct tar_stat_info const *st, char *output);
extern void xattrs_print (struct tar_stat_info const *st);

View File

@ -1,6 +1,6 @@
/* POSIX extended headers for tar.
Copyright (C) 2003-2026 Free Software Foundation, Inc.
Copyright (C) 2003-2023 Free Software Foundation, Inc.
This file is part of GNU tar.
@ -19,7 +19,6 @@
#include <system.h>
#include <c-ctype.h>
#include <fnmatch.h>
#include <hash.h>
#include <inttostr.h>
@ -36,7 +35,7 @@ static void code_string (char const *string, char const *keyword,
struct xheader *xhdr);
/* Number of global headers written so far. */
static intmax_t global_header_count;
static size_t global_header_count;
/* FIXME: Possibly it should be reset after changing the volume.
POSIX %n specification says that it is expanded to the sequence
number of current global header in *the* archive. However, for
@ -52,7 +51,7 @@ static intmax_t global_header_count;
/* Interface functions to obstacks */
static void
x_obstack_grow (struct xheader *xhdr, const char *ptr, idx_t length)
x_obstack_grow (struct xheader *xhdr, const char *ptr, size_t length)
{
obstack_grow (xhdr->stk, ptr, length);
xhdr->size += length;
@ -66,7 +65,7 @@ x_obstack_1grow (struct xheader *xhdr, char c)
}
static void
x_obstack_blank (struct xheader *xhdr, idx_t length)
x_obstack_blank (struct xheader *xhdr, size_t length)
{
obstack_blank (xhdr->stk, length);
xhdr->size += length;
@ -124,7 +123,7 @@ xheader_keyword_override_p (const char *keyword)
struct keyword_list *kp;
for (kp = keyword_override_list; kp; kp = kp->next)
if (streq (kp->pattern, keyword))
if (strcmp (kp->pattern, keyword) == 0)
return true;
return false;
}
@ -161,7 +160,7 @@ xheader_list_destroy (struct keyword_list **root)
static _Noreturn void
xheader_set_single_keyword (char *kw)
{
paxusage (_("Keyword %s is unknown or not yet implemented"), kw);
USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet implemented"), kw));
}
static void
@ -170,7 +169,7 @@ assign_time_option (char **sval, time_t *tval, const char *input)
char *p;
struct timespec t = decode_timespec (input, &p, false);
if (! valid_timespec (t) || *p)
paxerror (0, _("Time stamp is out of allowed range"));
ERROR ((0, 0, _("Time stamp is out of allowed range")));
else
{
*tval = t.tv_sec;
@ -185,7 +184,7 @@ xheader_set_keyword_equal (char *kw, char *eq)
char *p = eq;
if (eq == kw)
paxusage (_("Malformed pax option: %s"), quote (kw));
USAGE_ERROR ((0, 0, _("Malformed pax option: %s"), quote (kw)));
if (eq[-1] == ':')
{
@ -193,32 +192,32 @@ xheader_set_keyword_equal (char *kw, char *eq)
global = false;
}
while (p > kw && c_isspace (*p))
while (p > kw && isspace ((unsigned char) *p))
p--;
*p = 0;
for (p = eq + 1; *p && c_isspace (*p); p++)
for (p = eq + 1; *p && isspace ((unsigned char) *p); p++)
;
if (streq (kw, "delete"))
if (strcmp (kw, "delete") == 0)
{
if (xheader_protected_pattern_p (p))
paxusage (_("Pattern %s cannot be used"), quote (p));
USAGE_ERROR ((0, 0, _("Pattern %s cannot be used"), quote (p)));
xheader_list_append (&keyword_pattern_list, p, NULL);
}
else if (streq (kw, "exthdr.name"))
else if (strcmp (kw, "exthdr.name") == 0)
assign_string (&exthdr_name, p);
else if (streq (kw, "globexthdr.name"))
else if (strcmp (kw, "globexthdr.name") == 0)
assign_string (&globexthdr_name, p);
else if (streq (kw, "exthdr.mtime"))
else if (strcmp (kw, "exthdr.mtime") == 0)
assign_time_option (&exthdr_mtime_option, &exthdr_mtime, p);
else if (streq (kw, "globexthdr.mtime"))
else if (strcmp (kw, "globexthdr.mtime") == 0)
assign_time_option (&globexthdr_mtime_option, &globexthdr_mtime, p);
else
{
if (xheader_protected_keyword_p (kw))
paxusage (_("Keyword %s cannot be overridden"), kw);
USAGE_ERROR ((0, 0, _("Keyword %s cannot be overridden"), kw));
if (global)
xheader_list_append (&keyword_global_override_list, kw, p);
else
@ -254,9 +253,10 @@ xheader_set_option (char *string)
%% A '%' character. */
char *
xheader_format_name (struct tar_stat_info *st, const char *fmt, intmax_t n)
xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n)
{
char *buf;
size_t len;
char *q;
const char *p;
char *dirp = NULL;
@ -264,10 +264,10 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, intmax_t n)
char *base = NULL;
char pidbuf[UINTMAX_STRSIZE_BOUND];
char const *pptr = NULL;
char nbuf[INTMAX_STRSIZE_BOUND];
char nbuf[UINTMAX_STRSIZE_BOUND];
char const *nptr = NULL;
idx_t len = 0;
len = 0;
for (p = fmt; *p; p++)
{
if (*p == '%' && p[1])
@ -302,7 +302,7 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, intmax_t n)
break;
case 'n':
nptr = imaxtostr (n, nbuf);
nptr = umaxtostr (n, nbuf);
len += nbuf + sizeof nbuf - 1 - nptr;
break;
@ -383,13 +383,14 @@ enum {
pax_file_header,
pax_global_header
};
/* Return the name for the POSIX extended header T */
#define HEADER_TEMPLATE(t) header_template[t][posixly_correct]
char *
xheader_xhdr_name (struct tar_stat_info *st)
{
if (!exthdr_name)
assign_string (&exthdr_name,
header_template[pax_file_header][posixly_correct]);
assign_string (&exthdr_name, HEADER_TEMPLATE (pax_file_header));
return xheader_format_name (st, exthdr_name, 0);
}
@ -398,16 +399,15 @@ xheader_ghdr_name (void)
{
if (!globexthdr_name)
{
const char *global_header_template
= header_template[pax_global_header][posixly_correct];
size_t len;
const char *global_header_template = HEADER_TEMPLATE (pax_global_header);
const char *tmp = getenv ("TMPDIR");
if (!tmp)
tmp = "/tmp";
idx_t tmplen = strlen (tmp);
idx_t templatesize = strlen (global_header_template) + 1;
globexthdr_name = ximalloc (tmplen + templatesize);
char *p = mempcpy (globexthdr_name, tmp, tmplen);
memcpy (p, global_header_template, templatesize);
len = strlen (tmp) + strlen (global_header_template) + 1;
globexthdr_name = xmalloc (len);
strcpy(globexthdr_name, tmp);
strcat(globexthdr_name, global_header_template);
}
return xheader_format_name (NULL, globexthdr_name, global_header_count + 1);
@ -416,7 +416,11 @@ xheader_ghdr_name (void)
void
xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
{
idx_t size = xhdr->size;
union block *header;
size_t size;
char *p;
size = xhdr->size;
switch (type)
{
case XGLTYPE:
@ -429,17 +433,21 @@ xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
t = exthdr_mtime;
break;
}
union block *header = start_private_header (name, size, t);
header = start_private_header (name, size, t);
header->header.typeflag = type;
simple_finish_header (header);
char *p = xhdr->buffer;
p = xhdr->buffer;
do
{
size_t len;
header = find_next_block ();
idx_t len = min (size, BLOCKSIZE);
len = BLOCKSIZE;
if (len > size)
len = size;
memcpy (header->buffer, p, len);
if (len < BLOCKSIZE)
memset (header->buffer + len, 0, BLOCKSIZE - len);
@ -481,7 +489,7 @@ void
xheader_forbid_global (void)
{
if (keyword_global_override_list)
paxusage (_("can't update global extended header record"));
USAGE_ERROR ((0, 0, _("can't update global extended header record")));
}
/* This is reversal function for xattr_encode_keyword. See comment for
@ -524,18 +532,15 @@ xattr_decode_keyword (char *keyword)
/* General Interface */
enum
{
XHDR_PROTECTED = 0x01,
XHDR_GLOBAL = 0x02
};
#define XHDR_PROTECTED 0x01
#define XHDR_GLOBAL 0x02
struct xhdr_tab
{
char const *keyword;
void (*coder) (struct tar_stat_info const *, char const *,
struct xheader *, void const *data);
void (*decoder) (struct tar_stat_info *, char const *, char const *, idx_t);
void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t);
int flags;
bool prefix; /* select handler comparing prefix only */
};
@ -555,13 +560,13 @@ locate_handler (char const *keyword)
for (p = xhdr_tab; p->keyword; p++)
if (p->prefix)
{
idx_t kwlen = strlen (p->keyword);
size_t kwlen = strlen (p->keyword);
if (strncmp (p->keyword, keyword, kwlen) == 0 && keyword[kwlen] == '.')
return p;
}
else
{
if (streq (p->keyword, keyword))
if (strcmp (p->keyword, keyword) == 0)
return p;
}
@ -587,7 +592,7 @@ xheader_protected_keyword_p (const char *keyword)
for (p = xhdr_tab; p->keyword; p++)
if (!p->prefix && (p->flags & XHDR_PROTECTED)
&& streq (p->keyword, keyword))
&& strcmp (p->keyword, keyword) == 0)
return true;
return false;
}
@ -597,37 +602,34 @@ xheader_protected_keyword_p (const char *keyword)
static bool
decode_record (struct xheader *xhdr,
char **ptr,
void (*handler) (void *, char const *, char const *, idx_t),
void (*handler) (void *, char const *, char const *, size_t),
void *data)
{
char *start = *ptr;
char *p = start;
size_t len;
char *len_lim;
char const *keyword;
char *nextp;
idx_t len_max = xhdr->buffer + xhdr->size - start;
size_t len_max = xhdr->buffer + xhdr->size - start;
while (*p == ' ' || *p == '\t')
p++;
idx_t len = stoint (p, &len_lim, NULL, 0, IDX_MAX);
if (len_lim == p)
if (! ISDIGIT (*p))
{
/* The length is missing.
FIXME: Comment why this is diagnosed only if (*p), or change code. */
if (*p)
paxerror (0, _("Malformed extended header: missing length"));
ERROR ((0, 0, _("Malformed extended header: missing length")));
return false;
}
len = strtoumax (p, &len_lim, 10);
if (len_max < len)
{
/* Avoid giant diagnostics, as this won't help user. */
int len_len = min (len_lim - p, 1000);
paxerror (0, _("Extended header length %.*s is out of range"),
len_len, p);
int len_len = len_lim - p;
ERROR ((0, 0, _("Extended header length %.*s is out of range"),
len_len, p));
return false;
}
@ -637,7 +639,8 @@ decode_record (struct xheader *xhdr,
continue;
if (p == len_lim)
{
paxerror (0, _("Malformed extended header: missing blank after length"));
ERROR ((0, 0,
_("Malformed extended header: missing blank after length")));
return false;
}
@ -645,13 +648,13 @@ decode_record (struct xheader *xhdr,
p = strchr (p, '=');
if (! (p && p < nextp))
{
paxerror (0, _("Malformed extended header: missing equal sign"));
ERROR ((0, 0, _("Malformed extended header: missing equal sign")));
return false;
}
if (nextp[-1] != '\n')
{
paxerror (0, _("Malformed extended header: missing newline"));
ERROR ((0, 0, _("Malformed extended header: missing newline")));
return false;
}
@ -670,12 +673,12 @@ run_override_list (struct keyword_list *kp, struct tar_stat_info *st)
{
struct xhdr_tab const *t = locate_handler (kp->pattern);
if (t)
t->decoder (st, kp->pattern, kp->value, strlen (kp->value));
t->decoder (st, t->keyword, kp->value, strlen (kp->value));
}
}
static void
decx (void *data, char const *keyword, char const *value, idx_t size)
decx (void *data, char const *keyword, char const *value, size_t size)
{
struct xhdr_tab const *t;
struct tar_stat_info *st = data;
@ -688,9 +691,9 @@ decx (void *data, char const *keyword, char const *value, idx_t size)
if (t)
t->decoder (st, keyword, value, size);
else
warnopt (WARN_UNKNOWN_KEYWORD, 0,
_("Ignoring unknown extended header keyword %s"),
quotearg_style (shell_escape_always_quoting_style, keyword));
WARNOPT (WARN_UNKNOWN_KEYWORD,
(0, 0, _("Ignoring unknown extended header keyword '%s'"),
keyword));
}
void
@ -720,7 +723,7 @@ xheader_decode (struct tar_stat_info *st)
static void
decg (void *data, char const *keyword, char const *value,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
struct keyword_list **kwl = data;
struct xhdr_tab const *tab = locate_handler (keyword);
@ -774,26 +777,29 @@ xheader_store (char const *keyword, struct tar_stat_info *st,
void
xheader_read (struct xheader *xhdr, union block *p, off_t size)
{
idx_t j = 0;
size_t j = 0;
if (size < 0)
size = 0; /* Already diagnosed. */
idx_t size_plus_1;
if (ckd_add (&size_plus_1, size, BLOCKSIZE + 1))
if (SIZE_MAX - BLOCKSIZE <= size)
xalloc_die ();
size = size_plus_1 - 1;
size += BLOCKSIZE;
xhdr->size = size;
xhdr->buffer = xmalloc (size_plus_1);
xhdr->buffer = xmalloc (size + 1);
xhdr->buffer[size] = '\0';
do
{
if (!p)
paxfatal (0, _("Unexpected EOF in archive"));
size_t len = size;
if (len > BLOCKSIZE)
len = BLOCKSIZE;
if (!p)
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
idx_t len = min (size, BLOCKSIZE);
memcpy (&xhdr->buffer[j], p->buffer, len);
set_next_block_after (p);
@ -815,55 +821,66 @@ xheader_read (struct xheader *xhdr, union block *p, off_t size)
(http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html)
*/
static char *
xattr_encode_keyword (char const *keyword)
xattr_encode_keyword(const char *keyword)
{
static char *encode_buffer = NULL;
static idx_t encode_buffer_size = 0;
static size_t encode_buffer_size = 0;
size_t bp; /* keyword/buffer pointers */
for (idx_t bp = 0; ; )
if (!encode_buffer)
{
if (encode_buffer_size < bp + 3 /* enough for URL encoding also.. */)
encode_buffer = xpalloc (encode_buffer, &encode_buffer_size, 3, -1, 1);
char c = *keyword++;
switch (c)
{
case '%':
memcpy (encode_buffer + bp, "%25", 3);
bp += 3;
break;
case '=':
memcpy (encode_buffer + bp, "%3D", 3);
bp += 3;
break;
default:
encode_buffer[bp++] = c;
if (!c)
return encode_buffer;
break;
}
encode_buffer_size = 256;
encode_buffer = xmalloc (encode_buffer_size);
}
else
*encode_buffer = 0;
for (bp = 0; *keyword != 0; ++bp, ++keyword)
{
char c = *keyword;
if (bp + 3 /* enough for URL encoding also.. */ >= encode_buffer_size)
{
encode_buffer = x2realloc (encode_buffer, &encode_buffer_size);
}
if (c == '%')
{
strcpy (encode_buffer + bp, "%25");
bp += 2;
}
else if (c == '=')
{
strcpy (encode_buffer + bp, "%3D");
bp += 2;
}
else
encode_buffer[bp] = c;
}
encode_buffer[bp] = 0;
return encode_buffer;
}
static void
xheader_print_n (struct xheader *xhdr, char const *keyword,
char const *value, idx_t vsize)
char const *value, size_t vsize)
{
idx_t p;
idx_t n = 0;
char nbuf[INTMAX_STRSIZE_BOUND];
size_t p;
size_t n = 0;
char nbuf[UINTMAX_STRSIZE_BOUND];
char const *np;
size_t len, klen;
keyword = xattr_encode_keyword (keyword);
idx_t klen = strlen (keyword);
idx_t len = klen + vsize + 3; /* ' ' + '=' + '\n' */
klen = strlen (keyword);
len = klen + vsize + 3; /* ' ' + '=' + '\n' */
do
{
p = n;
np = imaxtostr (len + p, nbuf);
np = umaxtostr (len + p, nbuf);
n = nbuf + sizeof nbuf - 1 - np;
}
while (n != p);
@ -904,7 +921,7 @@ xheader_destroy (struct xheader *xhdr)
}
else
free (xhdr->buffer);
xhdr->buffer = NULL;
xhdr->buffer = 0;
xhdr->size = 0;
}
@ -923,24 +940,26 @@ xheader_string_add (struct xheader *xhdr, char const *s)
if (xhdr->buffer)
return;
xheader_init (xhdr);
idx_t slen = strlen (s);
xhdr->string_length += slen;
x_obstack_grow (xhdr, s, slen);
xhdr->string_length += strlen (s);
x_obstack_grow (xhdr, s, strlen (s));
}
bool
xheader_string_end (struct xheader *xhdr, char const *keyword)
{
idx_t p;
idx_t n = 0;
uintmax_t len;
uintmax_t p;
uintmax_t n = 0;
size_t size;
char nbuf[UINTMAX_STRSIZE_BOUND];
char const *np;
char *cp;
if (xhdr->buffer)
return false;
xheader_init (xhdr);
idx_t len = strlen (keyword) + xhdr->string_length + (sizeof " =\n" - 1);
len = strlen (keyword) + xhdr->string_length + 3; /* ' ' + '=' + '\n' */
do
{
@ -951,10 +970,18 @@ xheader_string_end (struct xheader *xhdr, char const *keyword)
while (n != p);
p = strlen (keyword) + n + 2;
size = p;
if (size != p)
{
ERROR ((0, 0,
_("Generated keyword/value pair is too long (keyword=%s, length=%s)"),
keyword, nbuf));
obstack_free (xhdr->stk, obstack_finish (xhdr->stk));
return false;
}
x_obstack_blank (xhdr, p);
x_obstack_1grow (xhdr, '\n');
char *cp = obstack_next_free (xhdr->stk);
cp -= xhdr->string_length + p + 1;
cp = (char*) obstack_next_free (xhdr->stk) - xhdr->string_length - p - 1;
memmove (cp + p, cp, xhdr->string_length);
cp = stpcpy (cp, np);
*cp++ = ' ';
@ -970,10 +997,15 @@ static void
out_of_range_header (char const *keyword, char const *value,
intmax_t minval, uintmax_t maxval)
{
char minval_buf[INT_BUFSIZE_BOUND (intmax_t)];
char maxval_buf[UINTMAX_STRSIZE_BOUND];
char *minval_string = imaxtostr (minval, minval_buf);
char *maxval_string = umaxtostr (maxval, maxval_buf);
/* TRANSLATORS: The first %s is the pax extended header keyword
(atime, gid, etc.). */
paxerror (0, _("Extended header %s=%s is out of range %jd..%ju"),
keyword, quote (value), minval, maxval);
ERROR ((0, 0, _("Extended header %s=%s is out of range %s..%s"),
keyword, value, minval_string, maxval_string));
}
static void
@ -1023,14 +1055,14 @@ decode_time (struct timespec *ts, char const *arg, char const *keyword)
out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t),
TYPE_MAXIMUM (time_t));
else
paxerror (0, _("Malformed extended header: invalid %s=%s"),
keyword, quote (arg));
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
keyword, arg));
return false;
}
if (*arg_lim)
{
paxerror (0, _("Malformed extended header: invalid %s=%s"),
keyword, quote (arg));
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
keyword, arg));
return false;
}
@ -1058,17 +1090,16 @@ decode_signed_num (intmax_t *num, char const *arg,
char const *keyword)
{
char *arg_lim;
bool overflow;
intmax_t u = stoint (arg, &arg_lim, &overflow, minval, maxval);
intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
if ((arg_lim == arg) | *arg_lim)
if (errno == EINVAL || *arg_lim)
{
paxerror (0, _("Malformed extended header: invalid %s=%s"),
keyword, quote (arg));
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
keyword, arg));
return false;
}
if (overflow)
if (errno == ERANGE)
{
out_of_range_header (keyword, arg, minval, maxval);
return false;
@ -1101,7 +1132,7 @@ static void
dummy_decoder (MAYBE_UNUSED struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
MAYBE_UNUSED char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
}
@ -1116,7 +1147,7 @@ static void
atime_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
struct timespec ts;
if (decode_time (&ts, arg, keyword))
@ -1135,7 +1166,7 @@ static void
gid_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
intmax_t u;
if (decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t),
@ -1154,7 +1185,7 @@ static void
gname_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&st->gname, arg);
}
@ -1170,7 +1201,7 @@ static void
linkpath_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&st->link_name, arg);
}
@ -1186,7 +1217,7 @@ static void
ctime_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
struct timespec ts;
if (decode_time (&ts, arg, keyword))
@ -1205,7 +1236,7 @@ static void
mtime_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
struct timespec ts;
if (decode_time (&ts, arg, keyword))
@ -1235,7 +1266,7 @@ static void
path_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
if (! st->sparse_name_done)
raw_path_decoder (st, arg);
@ -1245,7 +1276,7 @@ static void
sparse_path_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
st->sparse_name_done = true;
raw_path_decoder (st, arg);
@ -1262,7 +1293,7 @@ static void
size_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
@ -1281,7 +1312,7 @@ static void
uid_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
intmax_t u;
if (decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t),
@ -1300,7 +1331,7 @@ static void
uname_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&st->uname, arg);
}
@ -1316,7 +1347,7 @@ static void
sparse_size_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
@ -1338,7 +1369,7 @@ static void
sparse_numblocks_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, SIZE_MAX, keyword))
@ -1353,7 +1384,7 @@ static void
sparse_offset_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void const *data)
{
idx_t const *pi = data;
size_t const *pi = data;
code_num (st->sparse_map[*pi].offset, keyword, xhdr);
}
@ -1361,7 +1392,7 @@ static void
sparse_offset_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
@ -1369,8 +1400,8 @@ sparse_offset_decoder (struct tar_stat_info *st,
if (st->sparse_map_avail < st->sparse_map_size)
st->sparse_map[st->sparse_map_avail].offset = u;
else
paxerror (0, _("Malformed extended header: excess %s=%s"),
"GNU.sparse.offset", arg);
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
"GNU.sparse.offset", arg));
}
}
@ -1378,7 +1409,7 @@ static void
sparse_numbytes_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void const *data)
{
idx_t const *pi = data;
size_t const *pi = data;
code_num (st->sparse_map[*pi].numbytes, keyword, xhdr);
}
@ -1386,7 +1417,7 @@ static void
sparse_numbytes_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
@ -1394,8 +1425,8 @@ sparse_numbytes_decoder (struct tar_stat_info *st,
if (st->sparse_map_avail < st->sparse_map_size)
st->sparse_map[st->sparse_map_avail++].numbytes = u;
else
paxerror (0, _("Malformed extended header: excess %s=%s"),
keyword, arg);
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
keyword, arg));
}
}
@ -1403,28 +1434,35 @@ static void
sparse_map_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
bool offset = true;
int offset = 1;
struct sp_array e;
st->sparse_map_avail = 0;
while (true)
while (1)
{
intmax_t u;
char *delim;
bool overflow;
off_t u = stoint (arg, &delim, &overflow, 0, TYPE_MAXIMUM (off_t));
if (delim == arg)
if (!ISDIGIT (*arg))
{
paxerror (0, _("Malformed extended header: invalid %s=%s"),
keyword, quote (arg));
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
keyword, arg));
return;
}
errno = 0;
u = strtoimax (arg, &delim, 10);
if (TYPE_MAXIMUM (off_t) < u)
{
u = TYPE_MAXIMUM (off_t);
errno = ERANGE;
}
if (offset)
{
e.offset = u;
if (overflow)
if (errno == ERANGE)
{
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
return;
@ -1433,7 +1471,7 @@ sparse_map_decoder (struct tar_stat_info *st,
else
{
e.numbytes = u;
if (overflow)
if (errno == ERANGE)
{
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
return;
@ -1442,8 +1480,8 @@ sparse_map_decoder (struct tar_stat_info *st,
st->sparse_map[st->sparse_map_avail++] = e;
else
{
paxerror (0, _("Malformed extended header: excess %s=%s"),
keyword, quote (arg));
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
keyword, arg));
return;
}
}
@ -1454,10 +1492,9 @@ sparse_map_decoder (struct tar_stat_info *st,
break;
else if (*delim != ',')
{
paxerror (0,
_("Malformed extended header: invalid %s:"
" unexpected delimiter %c"),
keyword, *delim);
ERROR ((0, 0,
_("Malformed extended header: invalid %s: unexpected delimiter %c"),
keyword, *delim));
return;
}
@ -1465,10 +1502,9 @@ sparse_map_decoder (struct tar_stat_info *st,
}
if (!offset)
paxerror (0,
_("Malformed extended header: invalid %s:"
" odd number of values"),
keyword);
ERROR ((0, 0,
_("Malformed extended header: invalid %s: odd number of values"),
keyword));
}
static void
@ -1482,9 +1518,9 @@ static void
dumpdir_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
idx_t size)
size_t size)
{
st->dumpdir = ximalloc (size);
st->dumpdir = xmalloc (size);
memcpy (st->dumpdir, arg, size);
}
@ -1500,7 +1536,7 @@ static void
volume_label_decoder (MAYBE_UNUSED struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&volume_label, arg);
}
@ -1517,10 +1553,10 @@ volume_size_coder (MAYBE_UNUSED struct tar_stat_info const *st,
static void
volume_size_decoder (MAYBE_UNUSED struct tar_stat_info *st,
char const *keyword,
char const *arg, MAYBE_UNUSED idx_t size)
char const *arg, MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
continued_file_size = u;
}
@ -1537,10 +1573,10 @@ volume_offset_coder (MAYBE_UNUSED struct tar_stat_info const *st,
static void
volume_offset_decoder (MAYBE_UNUSED struct tar_stat_info *st,
char const *keyword,
char const *arg, MAYBE_UNUSED idx_t size)
char const *arg, MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
continued_file_offset = u;
}
@ -1548,7 +1584,7 @@ static void
volume_filename_decoder (MAYBE_UNUSED struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&continued_file_name, arg);
}
@ -1563,7 +1599,7 @@ xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
static void
xattr_selinux_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword, char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
decode_string (&st->cntx_name, arg);
}
@ -1578,7 +1614,7 @@ xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
static void
xattr_acls_a_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword,
char const *arg, idx_t size)
char const *arg, size_t size)
{
st->acls_a_ptr = xmemdup (arg, size + 1);
st->acls_a_len = size;
@ -1594,7 +1630,7 @@ xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword,
static void
xattr_acls_d_decoder (struct tar_stat_info *st,
MAYBE_UNUSED char const *keyword, char const *arg,
idx_t size)
size_t size)
{
st->acls_d_ptr = xmemdup (arg, size + 1);
st->acls_d_len = size;
@ -1604,8 +1640,7 @@ static void
xattr_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void const *data)
{
idx_t const *idx_data = data;
idx_t n = *idx_data;
size_t n = *(size_t *)data;
xheader_print_n (xhdr, keyword,
st->xattr_map.xm_map[n].xval_ptr,
st->xattr_map.xm_map[n].xval_len);
@ -1613,7 +1648,7 @@ xattr_coder (struct tar_stat_info const *st, char const *keyword,
static void
xattr_decoder (struct tar_stat_info *st,
char const *keyword, char const *arg, idx_t size)
char const *keyword, char const *arg, size_t size)
{
char *xkey;
@ -1638,10 +1673,10 @@ static void
sparse_major_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, INTMAX_MAX, keyword))
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
st->sparse_major = u;
}
@ -1656,10 +1691,10 @@ static void
sparse_minor_decoder (struct tar_stat_info *st,
char const *keyword,
char const *arg,
MAYBE_UNUSED idx_t size)
MAYBE_UNUSED size_t size)
{
uintmax_t u;
if (decode_num (&u, arg, INTMAX_MAX, keyword))
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
st->sparse_minor = u;
}

View File

@ -1,6 +1,6 @@
# Makefile for GNU tar regression tests.
# Copyright 1996-2026 Free Software Foundation, Inc.
# Copyright 1996-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -45,24 +45,21 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
## Test suite. ##
## ------------ ##
# You can generate the body of this macro with the following shell command:
# LC_ALL=C ls *.at */*.at | sed -e 's/^/ /' -e '$!s/$/\\/'
TESTSUITE_AT = \
testsuite.at\
compress.m4\
T-cd.at\
T-dir00.at\
T-dir01.at\
T-empty.at\
T-mult.at\
T-nest.at\
T-nonl.at\
T-null.at\
T-null2.at\
T-rec.at\
T-recurse.at\
T-zfile.at\
acls01.at\
acls02.at\
acls03.at\
T-nonl.at\
T-mult.at\
T-nest.at\
add-file.at\
append.at\
append01.at\
@ -71,15 +68,14 @@ TESTSUITE_AT = \
append04.at\
append05.at\
backup01.at\
capabs_raw01.at\
chtype.at\
comprec.at\
comperr.at\
checkpoint/defaults.at\
checkpoint/interval.at\
checkpoint/dot.at\
checkpoint/dot-compat.at\
checkpoint/dot-int.at\
checkpoint/dot.at\
checkpoint/interval.at\
chtype.at\
comperr.at\
comprec.at\
delete01.at\
delete02.at\
delete03.at\
@ -87,8 +83,6 @@ TESTSUITE_AT = \
delete05.at\
delete06.at\
difflink.at\
dirrem01.at\
dirrem02.at\
exclude.at\
exclude01.at\
exclude02.at\
@ -106,10 +100,6 @@ TESTSUITE_AT = \
exclude14.at\
exclude15.at\
exclude16.at\
exclude17.at\
exclude18.at\
exclude19.at\
exclude20.at\
extrac01.at\
extrac02.at\
extrac03.at\
@ -135,17 +125,13 @@ TESTSUITE_AT = \
extrac23.at\
extrac24.at\
extrac25.at\
extrac26.at\
extrac27.at\
extrac28.at\
extrac29.at\
extrac30.at\
extrac31.at\
filerem01.at\
filerem02.at\
grow.at\
dirrem01.at\
dirrem02.at\
gzip.at\
ignfail.at\
grow.at\
incremental.at\
incr01.at\
incr02.at\
incr03.at\
@ -157,8 +143,8 @@ TESTSUITE_AT = \
incr09.at\
incr10.at\
incr11.at\
incremental.at\
indexfile.at\
ignfail.at\
label01.at\
label02.at\
label03.at\
@ -202,16 +188,22 @@ TESTSUITE_AT = \
opcomp04.at\
opcomp05.at\
opcomp06.at\
positional01.at\
positional02.at\
positional03.at\
options.at\
options02.at\
options03.at\
owner.at\
pipe.at\
positional01.at\
positional02.at\
positional03.at\
recurs02.at\
recurse.at\
recurs02.at\
rename01.at\
rename02.at\
rename03.at\
rename04.at\
rename05.at\
rename06.at\
remfiles01.at\
remfiles02.at\
remfiles03.at\
@ -234,21 +226,12 @@ TESTSUITE_AT = \
remfiles09b.at\
remfiles09c.at\
remfiles10.at\
rename01.at\
rename02.at\
rename03.at\
rename04.at\
rename05.at\
rename06.at\
same-order01.at\
same-order02.at\
selacl01.at\
selnx01.at\
shortfile.at\
shortrec.at\
shortupd.at\
shortrec.at\
sigpipe.at\
skipdir.at\
sparse01.at\
sparse02.at\
sparse03.at\
@ -264,13 +247,6 @@ TESTSUITE_AT = \
sptrcreat.at\
sptrdiff00.at\
sptrdiff01.at\
star/gtarfail.at\
star/gtarfail2.at\
star/multi-fail.at\
star/pax-big-10g.at\
star/ustar-big-2g.at\
star/ustar-big-8g.at\
testsuite.at\
time01.at\
time02.at\
truncate.at\
@ -279,11 +255,21 @@ TESTSUITE_AT = \
update02.at\
update03.at\
update04.at\
volsize.at\
volume.at\
verbose.at\
verify.at\
version.at\
volsize.at\
volume.at\
xform-h.at\
xform01.at\
xform02.at\
xform03.at\
star/gtarfail.at\
star/gtarfail2.at\
star/multi-fail.at\
star/ustar-big-2g.at\
star/ustar-big-8g.at\
star/pax-big-10g.at\
xattr01.at\
xattr02.at\
xattr03.at\
@ -292,11 +278,12 @@ TESTSUITE_AT = \
xattr06.at\
xattr07.at\
xattr08.at\
xform-h.at\
xform01.at\
xform02.at\
xform03.at\
xform04.at
acls01.at\
acls02.at\
acls03.at\
selnx01.at\
selacl01.at\
capabs_raw01.at
distclean-local:
-rm -rf download
@ -304,7 +291,7 @@ distclean-local:
TESTSUITE = $(srcdir)/testsuite
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): compress.m4 package.m4 $(TESTSUITE_AT)
$(TESTSUITE): package.m4 $(TESTSUITE_AT)
$(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
mv $@.tmp $@
@ -345,7 +332,6 @@ AM_CPPFLAGS = \
-DLOCALEDIR=\"$(localedir)\"
LDADD = ../gnu/libgnu.a\
$(LIB_ACL) $(QCOPY_ACL_LIB) $(CLOCK_TIME_LIB) $(EUIDACCESS_LIBGEN)\
$(GETRANDOM_LIB) $(HARD_LOCALE_LIB) $(FILE_HAS_ACL_LIB) $(MBRTOWC_LIB)\
$(LIB_SELINUX) $(SETLOCALE_NULL_LIB) \
$(LIBINTL) $(LIBICONV)
$(LIB_ACL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)\
$(LIB_GETRANDOM) $(LIB_HARD_LOCALE) $(FILE_HAS_ACL_LIB) $(LIB_MBRTOWC)\
$(LIB_SELINUX) $(LIB_SETLOCALE_NULL)

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2014-2026 Free Software Foundation, Inc.
# Copyright 2014-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -43,3 +43,4 @@ dir/file1
dir/file2
])
AT_CLEANUP

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2014-2026 Free Software Foundation, Inc.
# Copyright 2014-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -43,3 +43,4 @@ dir/file1
dir/file2
])
AT_CLEANUP

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright 2006-2026 Free Software Foundation, Inc.
# Copyright 2006-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#
@ -43,3 +43,4 @@ file4
],[],[],[],[ustar])
AT_CLEANUP

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright 2006-2026 Free Software Foundation, Inc.
# Copyright 2006-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,5 +1,5 @@
# This file is part of test suite for GNU tar. -*- Autotest -*-
# Copyright 2015-2026 Free Software Foundation, Inc.
# Copyright 2015-2023 Free Software Foundation, Inc.
#
# GNU tar is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
# Test suite for GNU tar.
# Copyright 2015-2026 Free Software Foundation, Inc.
# Copyright 2015-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#
@ -37,7 +37,7 @@ b
tar cf archive -T empty -T valid
tar tf archive
echo "=="
tar cf archive -T valid -T empty
tar cf archive -T valid -T empty
tar tf archive
],
[0],

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2011-2026 Free Software Foundation, Inc.
# Copyright 2011-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2011-2026 Free Software Foundation, Inc.
# Copyright 2011-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2013-2026 Free Software Foundation, Inc.
# Copyright 2013-2023 Free Software Foundation, Inc.
# This file is part of GNU tar.
@ -73,9 +73,9 @@ tar --acls -cf acl.tar -C pure d1
# Directory names are chosen based on "how the files were extracted from
# archive". Equivalent no* tags are used also:
# ^sacl_ - extracted archive has stored ACLs
# _def_ - target directory (-C) has default ACLs
# _optacl$ - extraction was done with --acls option
# ^sacl_ extracted archive has stored ACLs
# _def_ target directory (-C) has default ACLs
# _optacl$ extraction was done with --acls option
mkdir sacl_def_optacl
mkdir sacl_def_optnoacl

View File

@ -1,7 +1,7 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2016-2026 Free Software Foundation, Inc.
# Copyright 2016-2023 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#

Some files were not shown because too many files have changed in this diff Show More