mirror of
https://https.git.savannah.gnu.org/git/tar.git
synced 2026-01-28 18:35:03 +00:00
Compare commits
298 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fca6143ea | ||
|
|
29bb75ed65 | ||
|
|
bd20771003 | ||
|
|
f1efd80ebe | ||
|
|
d55c5fd2c5 | ||
|
|
505cf47a0a | ||
|
|
f83a120c58 | ||
|
|
2bbc58bf0b | ||
|
|
85d99f18af | ||
|
|
db65c2dd68 | ||
|
|
28556dddae | ||
|
|
cdc541ad52 | ||
|
|
2e243986c7 | ||
|
|
db9ca8d754 | ||
|
|
b53b39209e | ||
|
|
8d8e441bb5 | ||
|
|
453d903de9 | ||
|
|
0521528cdf | ||
|
|
4e548d150c | ||
|
|
50b559c3d7 | ||
|
|
75b03fdff4 | ||
|
|
aec5d77437 | ||
|
|
8ba9e244e8 | ||
|
|
e1445cfdf0 | ||
|
|
915a8077af | ||
|
|
cdb586803b | ||
|
|
bdd773d028 | ||
|
|
382a47f2fd | ||
|
|
a109947a78 | ||
|
|
8fca2d35e8 | ||
|
|
238250f19e | ||
|
|
7c241126f1 | ||
|
|
56fb4a96ca | ||
|
|
83cad5835f | ||
|
|
58b471f14a | ||
|
|
f8b087a9f8 | ||
|
|
109edc9edb | ||
|
|
08e42808b6 | ||
|
|
0b7d124e36 | ||
|
|
ca6af3a7f5 | ||
|
|
92f040151c | ||
|
|
5def5cb369 | ||
|
|
f501cf8c9a | ||
|
|
2737e1aec0 | ||
|
|
ca02de4050 | ||
|
|
ea7cfcba77 | ||
|
|
bdc442bd5c | ||
|
|
5402831d62 | ||
|
|
4e742fc867 | ||
|
|
076818f8d9 | ||
|
|
c11084bcc2 | ||
|
|
75735940f1 | ||
|
|
8921131877 | ||
|
|
aecf7146d3 | ||
|
|
1ad538b359 | ||
|
|
7d96e820a5 | ||
|
|
7c4f8fb579 | ||
|
|
4303066730 | ||
|
|
9324b472b0 | ||
|
|
b009124ffd | ||
|
|
827dde1605 | ||
|
|
65228e9ba9 | ||
|
|
e36d3354c7 | ||
|
|
d175e21b7f | ||
|
|
c0fce47363 | ||
|
|
807e340ab2 | ||
|
|
6131dd2805 | ||
|
|
bfc3346394 | ||
|
|
b5f4948ce4 | ||
|
|
cd1f6624f7 | ||
|
|
55ecb28315 | ||
|
|
31d84e2f67 | ||
|
|
2e41cdce6d | ||
|
|
ff9d7ec77b | ||
|
|
4a9a4c16e1 | ||
|
|
0aa991f386 | ||
|
|
53f7e6aa62 | ||
|
|
c3f93039ca | ||
|
|
d2b6b7b0a7 | ||
|
|
9bbcac1cf7 | ||
|
|
ac06d4d104 | ||
|
|
a855a80d06 | ||
|
|
b5bf1ccd18 | ||
|
|
a6cf78b0fa | ||
|
|
568919d77b | ||
|
|
c500103600 | ||
|
|
5c47fcf187 | ||
|
|
005f2916b6 | ||
|
|
15d35a0f61 | ||
|
|
04b4f491a8 | ||
|
|
e531f8c66c | ||
|
|
f4ac66226a | ||
|
|
6993486ed8 | ||
|
|
04c1b85872 | ||
|
|
ef95115f61 | ||
|
|
41143ee46f | ||
|
|
f96aff3ce9 | ||
|
|
53a3691092 | ||
|
|
91ad4ea343 | ||
|
|
7eb4dbaff1 | ||
|
|
112ead7931 | ||
|
|
fd401e1d29 | ||
|
|
f8a679e942 | ||
|
|
3b0d006830 | ||
|
|
6e873de727 | ||
|
|
bde3e8d663 | ||
|
|
967f5f52f7 | ||
|
|
5a41310e57 | ||
|
|
3357683933 | ||
|
|
a337cd35a0 | ||
|
|
5a7185ae31 | ||
|
|
0aa69501d3 | ||
|
|
2339c9106b | ||
|
|
a3ba452f40 | ||
|
|
d9da938963 | ||
|
|
989842ff0d | ||
|
|
6f5718a35f | ||
|
|
d68c37b640 | ||
|
|
c0ef66da92 | ||
|
|
c2ce0b7e13 | ||
|
|
7b278044a7 | ||
|
|
025f19e6bd | ||
|
|
c61a2bee73 | ||
|
|
08a9174444 | ||
|
|
e0f9b0fdea | ||
|
|
17ad155fb2 | ||
|
|
303ac16ec0 | ||
|
|
6df7a72434 | ||
|
|
23582f3445 | ||
|
|
317e4d6a3c | ||
|
|
5f4a4164b7 | ||
|
|
7c0feaefd0 | ||
|
|
e513950080 | ||
|
|
2ce5791124 | ||
|
|
d127dac10e | ||
|
|
61a978f6d4 | ||
|
|
fae968bd2d | ||
|
|
9afbe6961c | ||
|
|
5704e5795a | ||
|
|
b73127edc4 | ||
|
|
dd71d3796d | ||
|
|
f73c927a71 | ||
|
|
04b92eca49 | ||
|
|
5a00343006 | ||
|
|
739483114d | ||
|
|
849f244a0b | ||
|
|
15dc3210cc | ||
|
|
7abf1420c3 | ||
|
|
0b60228081 | ||
|
|
f9ed22de9b | ||
|
|
78dd7bf0bc | ||
|
|
647cafff96 | ||
|
|
bd06b114d9 | ||
|
|
8767b1c84a | ||
|
|
e59d09db7d | ||
|
|
dd1bae32ce | ||
|
|
8b3073e1d2 | ||
|
|
7c4f884747 | ||
|
|
82ef07c9bd | ||
|
|
350cc4077e | ||
|
|
7f557428a4 | ||
|
|
dd0f95965d | ||
|
|
cdcd1580c8 | ||
|
|
dfb1da7253 | ||
|
|
79cb9aaab6 | ||
|
|
da109fae7a | ||
|
|
cc1352699a | ||
|
|
4323e98683 | ||
|
|
005e345c04 | ||
|
|
95a5f043c5 | ||
|
|
f25dd56e83 | ||
|
|
f1e4947992 | ||
|
|
0dfcfa4aa4 | ||
|
|
e9c16628f0 | ||
|
|
a0a1243c69 | ||
|
|
812a49419a | ||
|
|
541f3bc374 | ||
|
|
6bc4c4bf96 | ||
|
|
ab7a14bd92 | ||
|
|
b596676c78 | ||
|
|
15c6010c32 | ||
|
|
43231ae554 | ||
|
|
b201a37421 | ||
|
|
c9a3abcbe7 | ||
|
|
18dadeffc0 | ||
|
|
1521d3dae0 | ||
|
|
5ab90d6c96 | ||
|
|
e137c14285 | ||
|
|
95ebde4303 | ||
|
|
ef290cb171 | ||
|
|
09aec02e32 | ||
|
|
9b69d17e24 | ||
|
|
b3992e4ef8 | ||
|
|
88c2aa1616 | ||
|
|
d1e72a536f | ||
|
|
3ffe2eb073 | ||
|
|
eb9bb9bf80 | ||
|
|
4642cd04ed | ||
|
|
a80f364662 | ||
|
|
5316938142 | ||
|
|
83926613a4 | ||
|
|
9a2344b183 | ||
|
|
5182462cf1 | ||
|
|
0ab451a420 | ||
|
|
dab2830e38 | ||
|
|
9cef4d5495 | ||
|
|
7cda31b1e0 | ||
|
|
cc691f8272 | ||
|
|
390950282d | ||
|
|
f13f2d6815 | ||
|
|
c26c2ea2e9 | ||
|
|
51c841b927 | ||
|
|
aca308a778 | ||
|
|
414f635d8b | ||
|
|
281e03ec6c | ||
|
|
9cb1293628 | ||
|
|
44196e198f | ||
|
|
ba332e36d0 | ||
|
|
61656ef35b | ||
|
|
fbc60c2334 | ||
|
|
a78af4b95e | ||
|
|
c26111742a | ||
|
|
6c91bd82e1 | ||
|
|
aae99e863d | ||
|
|
39d315e8ea | ||
|
|
bf195d4ae4 | ||
|
|
a9372cf08a | ||
|
|
be1aa32c6d | ||
|
|
8a3fc52972 | ||
|
|
927d67855e | ||
|
|
c6a5af16ba | ||
|
|
dcc90722ac | ||
|
|
7557fdd4df | ||
|
|
91ee466c8a | ||
|
|
7079fc369b | ||
|
|
b26e798a0f | ||
|
|
f22b9fe3ce | ||
|
|
8f094605a8 | ||
|
|
26d1e4ddbc | ||
|
|
ec35690e91 | ||
|
|
3fa1fd0751 | ||
|
|
fd33f25989 | ||
|
|
45a86d45b2 | ||
|
|
afd073399a | ||
|
|
9f1c32c18b | ||
|
|
4e0deb7416 | ||
|
|
3d2c735b7c | ||
|
|
bd066ac0a5 | ||
|
|
b96cabb1ea | ||
|
|
42d1143dd5 | ||
|
|
2b9e2cc947 | ||
|
|
1752231f9e | ||
|
|
5f2cda027d | ||
|
|
1e6ce98e3a | ||
|
|
883f2e6dca | ||
|
|
628c49250a | ||
|
|
21318f3856 | ||
|
|
fac2b4c11a | ||
|
|
6ba24c31c6 | ||
|
|
7b65ae35ab | ||
|
|
f622c07108 | ||
|
|
d763055edd | ||
|
|
b4d1fa77b6 | ||
|
|
66eaa2f821 | ||
|
|
24a2fcfd83 | ||
|
|
835b0c7dee | ||
|
|
c6f0ad5117 | ||
|
|
f0098df0b3 | ||
|
|
3a27df1d69 | ||
|
|
c1e277476c | ||
|
|
783321ff1b | ||
|
|
01f986b921 | ||
|
|
05fcfaafb6 | ||
|
|
e35fe3a77c | ||
|
|
78d4ccd755 | ||
|
|
9599d193b8 | ||
|
|
e1bba5e7dd | ||
|
|
f2613580c7 | ||
|
|
1cdad4cc28 | ||
|
|
ecdef6677b | ||
|
|
5114218025 | ||
|
|
a9a8990fb3 | ||
|
|
12b58a69aa | ||
|
|
a5afb36765 | ||
|
|
bfee1d44a3 | ||
|
|
8131ca7b26 | ||
|
|
d437ecf75d | ||
|
|
8e5483577d | ||
|
|
9a30bb2674 | ||
|
|
eb30aa7801 | ||
|
|
68636f0bcb | ||
|
|
8ed95e92ef | ||
|
|
d1ca333391 | ||
|
|
71530f72d2 | ||
|
|
18f90676e4 | ||
|
|
7687bf4acc | ||
|
|
39849e9d91 | ||
|
|
8632df398b |
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,6 +1,6 @@
|
||||
[submodule "gnulib"]
|
||||
path = gnulib
|
||||
url = git://git.sv.gnu.org/gnulib.git
|
||||
url = https://git.savannah.gnu.org/git/gnulib.git
|
||||
[submodule "paxutils"]
|
||||
path = paxutils
|
||||
url = git://git.sv.gnu.org/paxutils.git
|
||||
url = https://git.savannah.gnu.org/git/paxutils.git
|
||||
|
||||
12
ChangeLog.1
12
ChangeLog.1
@ -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 Free Software Foundation, Inc.
|
||||
Copyright 1989-1997, 2013, 2023-2026 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, Jurgen Botz,
|
||||
Stefan Boleyn, Jason R. Mastaler, Joshua R. Poulson, Jürgen 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, Francois Pinard, Henrik Bakman,
|
||||
Reported by Cliff Krumvieda, François 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, Jurgen Botz,
|
||||
Jim Clausing, John J. Szetela, John R. Vanderpool, Jürgen 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, Francois Pinard, Henrik Bakman,
|
||||
Reported by Cliff Krumvieda, François 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 Haegg.
|
||||
Reported by Jörgen Hägg.
|
||||
|
||||
* rtape_lib.c (__rmt_open): Also look for /usr/bsd/rsh.
|
||||
Declare signal handler as returning void instead of int if USG is
|
||||
|
||||
@ -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 Free Software Foundation,
|
||||
Inc.
|
||||
Copyright 1997-2001, 2003-2009, 2013, 2023-2026 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 Joerg Weilbier
|
||||
* THANKS: Add Jörg 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 Joerg Schilling.
|
||||
Fix some C compatibility bugs reported by Jörg Schilling.
|
||||
|
||||
* src/common.h (stripped_prefix_len): Fix misspelling
|
||||
"stripped_path_len" in declaration.
|
||||
|
||||
@ -56,15 +56,3 @@ s/inadvertantly/inadvertently/
|
||||
|
||||
4dfcd6c054a5e9e1a371c822a3be90564dd9b690
|
||||
s/succesfully/successfully/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Main Makefile for GNU tar.
|
||||
|
||||
# Copyright 1994-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
74
NEWS
74
NEWS
@ -1,5 +1,73 @@
|
||||
GNU tar NEWS - User visible changes. 2023-07-18
|
||||
GNU tar NEWS - User visible changes. 2025-11-26
|
||||
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).
|
||||
|
||||
* 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.
|
||||
|
||||
* 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
|
||||
|
||||
@ -14,7 +82,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.
|
||||
|
||||
@ -1786,7 +1854,7 @@ Versions 1.07 back to 1.00 by Jay Fenlason.
|
||||
|
||||
|
||||
|
||||
Copyright 1994-2023 Free Software Foundation, Inc.
|
||||
Copyright 1994-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
2
README
2
README
@ -221,7 +221,7 @@ and share your findings by writing to <bug-tar@gnu.org>.
|
||||
|
||||
* Copying
|
||||
|
||||
Copyright 1990-2023 Free Software Foundation, Inc.
|
||||
Copyright 1990-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ Please send comments and problem reports to <bug-tar@gnu.org>.
|
||||
|
||||
|
||||
|
||||
Copyright 2001-2023 Free Software Foundation, Inc.
|
||||
Copyright 2001-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -61,10 +61,16 @@ 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-2023 Free Software Foundation, Inc.
|
||||
Copyright 2007-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
7
THANKS
7
THANKS
@ -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
|
||||
Jurgen Botz jbotz@orixa.mtholyoke.edu
|
||||
Jürgen 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,6 +308,7 @@ 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
|
||||
@ -327,6 +328,7 @@ 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
|
||||
@ -395,7 +397,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@twinsun.com
|
||||
Paul Eggert eggert@cs.ucla.edu
|
||||
Paul Kanz paul@icx.com
|
||||
Paul Mitchell P.Mitchell@surrey.ac.uk
|
||||
Paul Nevai pali+@osu.edu
|
||||
@ -535,6 +537,7 @@ 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
6
TODO
@ -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 Joerg Schilling's star implementation.
|
||||
* Interoperate better with Jörg 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 functonality.
|
||||
* Add tests for the new functionality.
|
||||
|
||||
* Consider this:
|
||||
|
||||
@ -45,7 +45,7 @@ Suggestions for improving GNU tar.
|
||||
|
||||
* Copyright notice
|
||||
|
||||
Copyright 2003-2023 Free Software Foundation, Inc.
|
||||
Copyright 2003-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
dnl Special Autoconf macros for GNU tar -*- autoconf -*-
|
||||
|
||||
dnl Copyright 2009-2023 Free Software Foundation, Inc.
|
||||
dnl Copyright 2009-2026 Free Software Foundation, Inc.
|
||||
dnl
|
||||
dnl This file is part of GNU tar.
|
||||
dnl
|
||||
|
||||
453
bootstrap
453
bootstrap
@ -3,9 +3,9 @@
|
||||
|
||||
# Bootstrap this package from checked-out sources.
|
||||
|
||||
scriptversion=2022-12-27.07; # UTC
|
||||
scriptversion=2025-06-10.02; # UTC
|
||||
|
||||
# Copyright (C) 2003-2023 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003-2026 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=2023-06-06.21; # UTC
|
||||
scriptlibversion=2025-12-04.19; # UTC
|
||||
|
||||
# Copyright (C) 2003-2023 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003-2026 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://git.savannah.gnu.org/git/gnulib.git
|
||||
default_gnulib_url=https://https.git.savannah.gnu.org/git/gnulib.git
|
||||
|
||||
# Copyright year, for the --version output.
|
||||
copyright_year=`echo "$scriptlibversion" | sed -e 's/[^0-9].*//'`
|
||||
@ -152,7 +152,8 @@ po_download_command_format=\
|
||||
"wget --mirror --level=1 -nd -nv -A.po -P '%s' \
|
||||
https://translationproject.org/latest/%s/"
|
||||
|
||||
# Prefer a non-empty tarname (4th argument of AC_INIT if given), else
|
||||
# When extracting the package name from an AC_INIT invocation,
|
||||
# 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(\[*/{
|
||||
@ -164,17 +165,20 @@ extract_package_name='
|
||||
q
|
||||
}
|
||||
s/[],)].*//
|
||||
s/^GNU //
|
||||
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
|
||||
s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
|
||||
p
|
||||
}
|
||||
'
|
||||
package=$(${AUTOCONF:-autoconf} --trace AC_INIT:\$4 configure.ac 2>/dev/null)
|
||||
normalize_package_name='
|
||||
s/^GNU //
|
||||
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
|
||||
s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g
|
||||
'
|
||||
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
|
||||
@ -478,10 +482,9 @@ 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
|
||||
}
|
||||
@ -497,92 +500,157 @@ 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"
|
||||
elif $use_git; then
|
||||
|| 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
|
||||
gnulib_path=$(git_modules_config submodule.gnulib.path)
|
||||
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 $?
|
||||
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 $?
|
||||
else
|
||||
# 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.
|
||||
# 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 $?
|
||||
else
|
||||
# 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 $?
|
||||
die "Error: submodule 'gnulib' has no configured url"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# GNULIB_REFDIR is not set or not usable. Ignore it.
|
||||
if git_modules_config submodule.gnulib.url >/dev/null; then
|
||||
gnulib_path='gnulib'
|
||||
if test ! -d "$gnulib_path"; then
|
||||
# The subdirectory 'gnulib' does not yet exist. Clone into it.
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
else
|
||||
if git fetch -h 2>&1 | grep -- --depth > /dev/null; then
|
||||
shallow='--depth 2'
|
||||
# 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
|
||||
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
|
||||
GNULIB_SRCDIR=$gnulib_path
|
||||
# Verify that the submodule contains a gnulib checkout.
|
||||
# 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"
|
||||
|| die "Error: '$gnulib_path' is supposed to contain a gnulib checkout," \
|
||||
"but does not contain gnulib-tool"
|
||||
GNULIB_SRCDIR=$gnulib_path
|
||||
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.
|
||||
}
|
||||
@ -594,7 +662,8 @@ 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"; \
|
||||
}
|
||||
@ -611,10 +680,18 @@ upgrade_bootstrap ()
|
||||
a) ignored=--;;
|
||||
*) ignored=ignored;;
|
||||
esac
|
||||
u=$update_lib
|
||||
exec sh -c \
|
||||
'{ 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}" "$@"' \
|
||||
'{ 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}" "$@"' \
|
||||
$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" \
|
||||
@ -635,7 +712,8 @@ fi
|
||||
autopull_usage() {
|
||||
cat <<EOF
|
||||
Usage: $me [OPTION]...
|
||||
Bootstrap this package from the checked-out sources.
|
||||
Bootstrap this package from the checked-out sources, phase 1:
|
||||
Pull files from the network.
|
||||
|
||||
Optional environment variables:
|
||||
GNULIB_SRCDIR Specifies the local directory where gnulib
|
||||
@ -647,21 +725,24 @@ 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 Cloneable URL of the gnulib repository.
|
||||
GNULIB_URL URL of the gnulib repository. The default is
|
||||
$default_gnulib_url,
|
||||
which is Gnulib's upstream 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 or the --gnulib-srcdir option
|
||||
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 points to a gnulib repository
|
||||
with the correct revision.
|
||||
--skip-po Do not download *.po files.
|
||||
EOF
|
||||
bootstrap_print_option_usage_hook
|
||||
cat <<EOF
|
||||
@ -673,21 +754,21 @@ are honored.
|
||||
|
||||
Gnulib sources can be fetched in various ways:
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
* 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 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.
|
||||
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.
|
||||
|
||||
* 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.
|
||||
to $default_gnulib_url; if GNULIB_REFDIR is set and is a git repository
|
||||
its contents may be used to accelerate the process.
|
||||
If the configuration variable GNULIB_REVISION is set in bootstrap.conf,
|
||||
then that revision is checked out.
|
||||
|
||||
@ -747,9 +828,12 @@ 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."
|
||||
@ -760,7 +844,7 @@ autopull()
|
||||
if $use_gnulib || $bootstrap_sync; then
|
||||
prepare_GNULIB_SRCDIR
|
||||
if $bootstrap_sync; then
|
||||
upgrade_bootstrap
|
||||
upgrade_bootstrap "$@"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -777,7 +861,8 @@ 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'
|
||||
git config merge.merge-changelog.driver \
|
||||
'git-merge-changelog %O %A %B "%Y"'
|
||||
else
|
||||
echo "$0: consider installing git-merge-changelog from gnulib"
|
||||
fi
|
||||
@ -811,7 +896,10 @@ autopull()
|
||||
uninitialized=`echo "$uninitialized" | grep -v '^gnulib$'`
|
||||
fi
|
||||
if test -n "$uninitialized"; then
|
||||
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."
|
||||
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."
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -849,9 +937,7 @@ update_po_files() {
|
||||
&& ls "$ref_po_dir"/*.po 2>/dev/null |
|
||||
sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return
|
||||
|
||||
langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g')
|
||||
test "$langs" = '*' && langs=x
|
||||
for po in $langs; do
|
||||
for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do
|
||||
case $po in x) continue;; esac
|
||||
new_po="$ref_po_dir/$po.po"
|
||||
cksum_file="$ref_po_dir/$po.s1"
|
||||
@ -870,7 +956,8 @@ update_po_files() {
|
||||
autogen_usage() {
|
||||
cat <<EOF
|
||||
Usage: $me [OPTION]...
|
||||
Bootstrap this package from the checked-out sources.
|
||||
Bootstrap this package from the checked-out sources, phase 2:
|
||||
Generate files from local files (no network access).
|
||||
|
||||
Optional environment variables:
|
||||
GNULIB_SRCDIR Specifies the local directory where gnulib
|
||||
@ -879,9 +966,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
|
||||
@ -1075,7 +1162,8 @@ 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."
|
||||
@ -1083,7 +1171,8 @@ 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
|
||||
@ -1212,6 +1301,20 @@ 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"
|
||||
@ -1235,7 +1338,7 @@ autogen()
|
||||
|
||||
# Invoke autoreconf with --force --install to ensure upgrades of tools
|
||||
# such as ylwrap.
|
||||
AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS"
|
||||
AUTORECONFFLAGS="--verbose --install --force $ACLOCAL_FLAGS"
|
||||
AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive"
|
||||
|
||||
# Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
|
||||
@ -1297,7 +1400,7 @@ autogen()
|
||||
|| die 'cannot generate runtime-po/Makevars'
|
||||
|
||||
# Copy identical files from po to runtime-po.
|
||||
(cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po)
|
||||
cp -p po/Makefile.in.in po/*-quot po/*.header po/*.sed po/*.sin runtime-po
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -1309,9 +1412,9 @@ autogen()
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# Local Variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
|
||||
# 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:
|
||||
@ -1325,42 +1428,55 @@ 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
|
||||
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 network
|
||||
--gen Do phase 2: generate from local files.
|
||||
(The default is to do both phases.)
|
||||
|
||||
--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 specify the local directory where a gnulib
|
||||
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.
|
||||
Defaults to \$GNULIB_REFDIR
|
||||
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).
|
||||
|
||||
--bootstrap-sync if this bootstrap script is not identical to
|
||||
Options:
|
||||
|
||||
--pull Do phase 1: Pull files from the network.
|
||||
--gen Do phase 2: Generate files from local files
|
||||
(no network access).
|
||||
(The default is to do both phases.)
|
||||
|
||||
--gnulib-srcdir=DIRNAME 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. Defaults
|
||||
to \$GNULIB_SRCDIR.
|
||||
--gnulib-refdir=DIRNAME 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.
|
||||
Defaults to \$GNULIB_REFDIR.
|
||||
Only used for phase 1 (--pull).
|
||||
|
||||
--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
|
||||
--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.
|
||||
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
|
||||
\$GNULIB_SRCDIR or the --gnulib-srcdir option
|
||||
points to a gnulib repository with the correct
|
||||
revision
|
||||
--skip-po do not download po files
|
||||
revision.
|
||||
Only used for phase 1 (--pull).
|
||||
--skip-po Do not download *.po files.
|
||||
Only used for phase 1 (--pull).
|
||||
EOF
|
||||
bootstrap_print_option_usage_hook
|
||||
cat <<EOF
|
||||
@ -1372,11 +1488,11 @@ are honored.
|
||||
|
||||
Gnulib sources can be fetched in various ways:
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
* Otherwise, if this package is in a git repository with a 'gnulib'
|
||||
submodule configured, then that submodule is initialized and updated
|
||||
@ -1386,7 +1502,8 @@ 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.
|
||||
to $default_gnulib_url; if GNULIB_REFDIR is set and is a git repository
|
||||
its contents may be used to accelerate the process.
|
||||
If the configuration variable GNULIB_REVISION is set in bootstrap.conf,
|
||||
then that revision is checked out.
|
||||
|
||||
@ -1458,9 +1575,11 @@ 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."
|
||||
@ -1470,7 +1589,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
|
||||
@ -1483,7 +1602,11 @@ 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` \
|
||||
@ -1491,18 +1614,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)
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
|
||||
# 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:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Bootstrap configuration for GNU tar.
|
||||
|
||||
# Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -20,10 +20,12 @@
|
||||
source_base=gnu
|
||||
gnulib_name=libgnu
|
||||
|
||||
# We don't need these modules, even though gnulib-tool mistakenly
|
||||
# includes them because of gettext dependencies.
|
||||
# We don't need these modules, even though gnulib-tool ordinarily
|
||||
# includes them because of dependencies on the modules 'exclude' and 'regex'.
|
||||
avoided_gnulib_modules='
|
||||
--avoid=lock
|
||||
--avoid=mbuiter
|
||||
--avoid=mbuiterf
|
||||
'
|
||||
|
||||
|
||||
|
||||
54
configure.ac
54
configure.ac
@ -1,6 +1,6 @@
|
||||
# Configure template for GNU tar. -*- autoconf -*-
|
||||
|
||||
# Copyright 1991, 1994-2010, 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1991, 1994-2010, 2013-2026 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], [bug-tar@gnu.org])
|
||||
AC_INIT([GNU tar], [1.35.90], [bug-tar@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/tar.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@ -34,19 +34,7 @@ AC_PROG_YACC
|
||||
gl_EARLY
|
||||
AC_CHECK_TOOLS([AR], [ar])
|
||||
|
||||
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_CHECK_HEADERS_ONCE([linux/fd.h sys/mtio.h])
|
||||
|
||||
AC_HEADER_MAJOR
|
||||
|
||||
@ -94,12 +82,16 @@ AC_TYPE_MODE_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_UID_T
|
||||
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)
|
||||
|
||||
# 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])
|
||||
|
||||
gt_TYPE_SSIZE_T
|
||||
|
||||
@ -120,9 +112,8 @@ AC_DEFINE([GNULIB_WCHAR_SINGLE_LOCALE], [1],
|
||||
like mbrtowc only after setting the locale, and never change the
|
||||
locale once set.])
|
||||
|
||||
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
|
||||
if test $COMPILE_ERROR_C = 1; then
|
||||
# This means that Gnulib's 'error' function will be used. It 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.
|
||||
@ -179,7 +170,6 @@ 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])
|
||||
@ -235,13 +225,7 @@ fi
|
||||
|
||||
TAR_HEADERS_ATTR_XATTR_H
|
||||
|
||||
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_CHECK_FUNCS_ONCE([fchmod fchown fsync mkfifo waitpid])
|
||||
|
||||
AC_ARG_VAR([RSH], [Configure absolute path to default remote shell binary])
|
||||
AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
|
||||
@ -265,9 +249,7 @@ AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
if test $tar_cv_path_RSH = no; then
|
||||
AC_CHECK_HEADERS(netdb.h)
|
||||
else
|
||||
if test $tar_cv_path_RSH != "no"; then
|
||||
AC_DEFINE_UNQUOTED(REMOTE_SHELL, "$tar_cv_path_RSH",
|
||||
[Define to the full path of your rsh, if any.])
|
||||
fi
|
||||
@ -361,7 +343,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_QUOTING_STYLE, $DEFAULT_QUOTING_STYLE,
|
||||
|
||||
# Iconv
|
||||
AM_ICONV
|
||||
AC_CHECK_HEADERS(iconv.h)
|
||||
AC_CHECK_HEADERS_ONCE([iconv.h])
|
||||
AC_CHECK_TYPE(iconv_t,:,
|
||||
AC_DEFINE(iconv_t, int,
|
||||
[Conversion descriptor type]),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
%%comments:
|
||||
Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
Copyright 2004-2026 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@twinsun.com>,
|
||||
Sergey Poznyakoff <gray@Mirddin.farlep.net>
|
||||
Paul Eggert <eggert@cs.ucla.edu>,
|
||||
Sergey Poznyakoff <gray@gnu.org>
|
||||
|
||||
%%contributors: Jay Fenlason,
|
||||
Joy Kendall,
|
||||
Francois Pinard <pinard@iro.umontreal.ca>
|
||||
François Pinard
|
||||
|
||||
%%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
2
doc/.gitignore
vendored
@ -23,5 +23,7 @@ tar.toc
|
||||
tar.tp
|
||||
tar.vr
|
||||
version.texi
|
||||
/gendocs_template
|
||||
/gendocs_template_min
|
||||
/parse-datetime.texi
|
||||
/rmt.8
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar documentation.
|
||||
|
||||
# Copyright 1994-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -138,7 +138,7 @@ check-docs:
|
||||
clean-local:
|
||||
rm -rf manual
|
||||
|
||||
GENDOCS=$(srcdir)/gendocs.sh
|
||||
GENDOCS=$(top_srcdir)/build-aux/gendocs.sh
|
||||
|
||||
TEXI2DVI=texi2dvi -E
|
||||
|
||||
@ -152,4 +152,3 @@ manual:
|
||||
$(GENDOCS) --texi2html tar 'GNU tar manual'
|
||||
|
||||
manual-rebuild: clean-local manual
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2026 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.
|
||||
|
||||
@ -5,8 +5,7 @@
|
||||
@c hence no sectioning command or @node.
|
||||
|
||||
@display
|
||||
Copyright @copyright{} 2000--2002, 2007--2008, 2022 Free Software
|
||||
Foundation, Inc.
|
||||
Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
|
||||
@uref{https://fsf.org/}
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
|
||||
512
doc/gendocs.sh
512
doc/gendocs.sh
@ -1,512 +0,0 @@
|
||||
#!/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:
|
||||
@ -1,101 +0,0 @@
|
||||
<!--#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 & GNU inquiries to
|
||||
<a href="mailto:gnu@gnu.org"><gnu@gnu.org></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%%"><%%EMAIL%%></a>.</p>
|
||||
</div>
|
||||
|
||||
<p>Copyright © 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>
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2026 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.1 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
;;; mastermenu.el --- Redefinition of texinfo-master-menu-list
|
||||
|
||||
;; Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
;; Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Sergey Poznyakoff
|
||||
;; Maintainer: bug-tar@gnu.org
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the GNU tar manual.
|
||||
@c Copyright (C) 2017--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2017--2026 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}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of GNU tar manual.
|
||||
@c Copyright 1992--2023 Free Software Foundation, Inc.
|
||||
@c Copyright 1992--2026 Free Software Foundation, Inc.
|
||||
@c See file tar.texi for copying conditions.
|
||||
|
||||
@c This file contains support for 'renditions' by Fran@,{c}ois Pinard
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2005--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2005--2026 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.)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2026 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.1 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2007--2023 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2007--2026 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
|
||||
|
||||
81
doc/tar.1
81
doc/tar.1
@ -1,5 +1,4 @@
|
||||
.\" 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
|
||||
@ -13,7 +12,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 "July 11, 2022" "TAR" "GNU TAR Manual"
|
||||
.TH TAR 1 "November 13, 2025" "TAR" "GNU TAR Manual"
|
||||
.SH NAME
|
||||
tar \- an archiving utility
|
||||
.SH SYNOPSIS
|
||||
@ -21,7 +20,6 @@ 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...]
|
||||
@ -36,7 +34,6 @@ 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...]
|
||||
@ -83,11 +80,9 @@ 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
|
||||
@ -122,20 +117,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
|
||||
@ -152,11 +147,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
|
||||
@ -187,7 +182,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
|
||||
current working directory is assumed.
|
||||
working directory is assumed.
|
||||
.TP
|
||||
\fB\-\-delete\fR
|
||||
Delete from the archive. The arguments supply names of the archive
|
||||
@ -228,7 +223,6 @@ 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
|
||||
@ -281,7 +275,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
|
||||
@ -447,7 +441,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
|
||||
@ -597,7 +591,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
|
||||
@ -608,7 +602,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
|
||||
@ -631,7 +625,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
|
||||
@ -644,7 +638,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
|
||||
@ -670,7 +664,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
|
||||
@ -748,7 +742,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.
|
||||
@ -854,17 +848,15 @@ 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
|
||||
@ -876,7 +868,15 @@ 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.
|
||||
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
|
||||
.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,7 +1249,6 @@ 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
|
||||
@ -1265,7 +1264,6 @@ 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.
|
||||
@ -1277,10 +1275,11 @@ 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.
|
||||
@ -1291,7 +1290,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.
|
||||
@ -1320,7 +1319,7 @@ found at:
|
||||
.SH "BUG REPORTS"
|
||||
Report bugs to <bug\-tar@gnu.org>.
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2023 Free Software Foundation, Inc.
|
||||
Copyright \(co 2013\(en2026 Free Software Foundation, Inc.
|
||||
.br
|
||||
.na
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
|
||||
529
doc/tar.texi
529
doc/tar.texi
@ -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--2023 Free Software Foundation, Inc.
|
||||
2021--2026 Free Software Foundation, Inc.
|
||||
|
||||
@quotation
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
@ -346,6 +346,7 @@ 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
|
||||
@ -1018,7 +1019,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, fileutils, GNU file utilities}).
|
||||
format=verbose, Verbose listing, coreutils, GNU core 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
|
||||
@ -1398,7 +1399,7 @@ practice/collection.tar
|
||||
@end smallexample
|
||||
|
||||
Note that the archive thus created is not in the subdirectory
|
||||
@file{practice}, but rather in the current working directory---the
|
||||
@file{practice}, but rather in the 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
|
||||
@ -1420,7 +1421,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 current directory before putting any files into
|
||||
@file{foo.tar} in the working 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
|
||||
@ -1848,7 +1849,7 @@ $ @kbd{tar --extract --file=music.tar --strip-components=1 folk}
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
will extract the file @file{folk} into the current working directory.
|
||||
will extract the file @file{folk} into the working directory.
|
||||
|
||||
@node going further
|
||||
@section Going Further Ahead in this Manual
|
||||
@ -2661,13 +2662,14 @@ 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. @xref{dereference}.
|
||||
itself. This a dangerous option, as it can cause @command{tar} to
|
||||
access files outside the working directory. @xref{dereference}.
|
||||
|
||||
@opsummary{directory}
|
||||
@item --directory=@var{dir}
|
||||
@itemx -C @var{dir}
|
||||
|
||||
When this option is specified, @command{tar} will change its current directory
|
||||
When this option is specified, @command{tar} will change its working directory
|
||||
to @var{dir} before performing any operations. When this option is used
|
||||
during archive creation, it is order sensitive. @xref{directory}.
|
||||
|
||||
@ -2720,7 +2722,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 itssubdirectories.
|
||||
The patterns affect the directory and all its subdirectories.
|
||||
@xref{exclude}.
|
||||
|
||||
@opsummary{exclude-tag}
|
||||
@ -2806,7 +2808,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
|
||||
|
||||
@ -3048,8 +3050,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 only be used for files whose
|
||||
modification times are later than @var{date}.
|
||||
modification times, and @var{date} will be used only for files with
|
||||
modification times later than @var{date}.
|
||||
|
||||
@opsummary{multi-volume}
|
||||
@item --multi-volume
|
||||
@ -3266,7 +3268,7 @@ 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 current
|
||||
directories that are on different file systems from the working
|
||||
directory.
|
||||
|
||||
@opsummary{one-top-level}
|
||||
@ -3525,7 +3527,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 reproducible.
|
||||
makes archive creation more reproducible. @xref{Reproducibility}.
|
||||
|
||||
@item inode
|
||||
Sort the directory entries on inode number. Sorting directories on
|
||||
@ -3635,7 +3637,7 @@ $ @kbd{tar cf archive.tar --transform 's,^\./,usr/,' .}
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
will add to @file{archive} files from the current working directory,
|
||||
will add to @file{archive} files from the working directory,
|
||||
replacing initial @samp{./} prefix with @samp{usr/}. For the detailed
|
||||
discussion, @xref{transform}.
|
||||
|
||||
@ -3913,7 +3915,7 @@ and @command{tar} were invoked as follows:
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
then the file @file{README} would be looked up in the current working
|
||||
then the file @file{README} would be looked up in the working
|
||||
directory, and files @file{main.c} and @file{Makefile} would be looked
|
||||
up in the directory @file{src}.
|
||||
|
||||
@ -4027,7 +4029,7 @@ successfully. For example, @w{@samp{tar --version}} might print:
|
||||
|
||||
@smallexample
|
||||
tar (GNU tar) @value{VERSION}
|
||||
Copyright (C) 2013-2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 2026 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.
|
||||
@ -4214,6 +4216,11 @@ 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:
|
||||
|
||||
@ -4633,7 +4640,7 @@ The subsections below discuss allowed values for @var{keyword} along with the
|
||||
warning messages they control.
|
||||
|
||||
@menu
|
||||
* General Warnings:: Keywords applicable for @command{tar --create}.
|
||||
* General Warnings:: Keywords controlling @command{tar} operation.
|
||||
* 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.
|
||||
@ -4664,6 +4671,11 @@ 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
|
||||
@ -4962,7 +4974,7 @@ Books, ISBN 1-56884-203-1.}. The two most common errors are:
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
Mistakingly using @code{create} instead of @code{extract}, when the
|
||||
Mistakenly 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
|
||||
@ -5578,7 +5590,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, fileutils, @acronym{GNU} file utilities}. This reference
|
||||
permissions, coreutils, @acronym{GNU} core 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
|
||||
@ -5592,28 +5604,27 @@ $ @kbd{tar -c -f archive.tar --mode='a+rw' .}
|
||||
@item --mtime=@var{date}
|
||||
@opindex mtime
|
||||
|
||||
When adding files to an archive, @command{tar} will use @var{date} as
|
||||
When adding files to an archive, @command{tar} uses @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 will be used.
|
||||
of that file is used.
|
||||
|
||||
The following example will set the modification date to 00:00:00,
|
||||
The following example sets the modification date to 00:00:00 @sc{utc} on
|
||||
January 1, 1970:
|
||||
|
||||
@smallexample
|
||||
$ @kbd{tar -c -f archive.tar --mtime='1970-01-01' .}
|
||||
$ @kbd{tar -c -f archive.tar --mtime='@@0' .}
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
When used with @option{--verbose} (@pxref{verbose tutorial}) @GNUTAR{}
|
||||
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.
|
||||
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.
|
||||
|
||||
For example:
|
||||
|
||||
@ -5625,14 +5636,15 @@ tar: Option --mtime: Treating date 'yesterday' as 2006-06-20
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
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
|
||||
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
|
||||
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_DATE_EPOCH .}
|
||||
$ @kbd{tar -c -f archive.tar --clamp-mtime --mtime="$SOURCE_EPOCH" .}
|
||||
@end smallexample
|
||||
|
||||
@item --owner=@var{user}
|
||||
@ -7810,7 +7822,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 current working directory.
|
||||
the contents of the working directory.
|
||||
|
||||
If you specify any other operation, @command{tar} does nothing.
|
||||
|
||||
@ -7858,7 +7870,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.info,GNU Findutils}, for details.} and put that list into a file
|
||||
@samp{m}, etc. @xref{Size,,,find,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
|
||||
@ -8123,7 +8135,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.
|
||||
|
||||
@ -8131,7 +8143,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
|
||||
@ -8212,9 +8224,19 @@ 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 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}.
|
||||
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/}}.
|
||||
|
||||
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.
|
||||
@ -8654,7 +8676,7 @@ a\backslash
|
||||
@end smallexample
|
||||
|
||||
Here is how usual @command{ls} command would have listed them, if they
|
||||
had existed in the current working directory:
|
||||
had existed in the working directory:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
@ -8882,7 +8904,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 current working directory. To do so, you type:
|
||||
the working directory. To do so, you type:
|
||||
|
||||
@smallexample
|
||||
$ @kbd{tar -xf usr.tar --strip=2 usr/include/stdlib.h}
|
||||
@ -8890,7 +8912,9 @@ $ @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.
|
||||
name, before extracting it. Notice, that archive members to extract are
|
||||
searched before that modification, hence the file name is specified in
|
||||
full.
|
||||
|
||||
If you add the @option{--verbose} (@option{-v}) option to the invocation
|
||||
above, you will note that the verbose listing still contains the
|
||||
@ -8919,7 +8943,7 @@ stdlib.h
|
||||
@end smallexample
|
||||
|
||||
Notice that in both cases the file @file{stdlib.h} is extracted to the
|
||||
current working directory, @option{--show-transformed-names} affects
|
||||
working directory, @option{--show-transformed-names} affects
|
||||
only the way its name is displayed.
|
||||
|
||||
This option is especially useful for verifying whether the invocation
|
||||
@ -9006,7 +9030,6 @@ 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,
|
||||
@ -9140,12 +9163,48 @@ 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
|
||||
|
||||
@ -9163,7 +9222,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 only want @command{tar} to make the date comparison based on
|
||||
If you want @command{tar} to make the date comparison based only on
|
||||
modification of the file's data (rather than status
|
||||
changes), then use the @option{--newer-mtime=@var{date}} option.
|
||||
|
||||
@ -9190,7 +9249,7 @@ name; the data modification time of that file is used as the date.
|
||||
|
||||
@opindex newer-mtime
|
||||
@item --newer-mtime=@var{date}
|
||||
Acts like @option{--after-date}, but only looks at data modification times.
|
||||
Act like @option{--after-date}, but look only at data modification times.
|
||||
@end table
|
||||
|
||||
These options limit @command{tar} to operate only on files which have
|
||||
@ -9209,8 +9268,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} only checks @code{mtime} and
|
||||
disregards @code{ctime}. Neither does it use @code{atime} (the last time the
|
||||
@var{date}, while @option{--newer-mtime} checks only @code{mtime} and
|
||||
disregards @code{ctime}. Neither option uses @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
|
||||
@ -9223,11 +9282,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{} 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:
|
||||
(@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:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
@ -9387,7 +9446,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 current
|
||||
will place the files @file{grape} and @file{prune} from the working
|
||||
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
|
||||
@ -9397,7 +9456,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 current directory.
|
||||
--extract}, all three files will be written in the working directory.
|
||||
|
||||
Contrast this with the command,
|
||||
|
||||
@ -9429,8 +9488,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 current directory, which might not be the same as
|
||||
the original current working directory of @command{tar}, due to a previous
|
||||
relative to the then working directory, which might not be the same as
|
||||
the original working directory of @command{tar}, due to a previous
|
||||
@option{--directory} option.
|
||||
|
||||
When using @option{--files-from} (@pxref{files}), you can put various
|
||||
@ -9469,7 +9528,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 @file{..}
|
||||
input or output, and complains about file names containing a @samp{..}
|
||||
component. There is an option that turns off this behavior:
|
||||
|
||||
@table @option
|
||||
@ -9477,7 +9536,8 @@ 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 @file{..} file name component.
|
||||
containing a @samp{..} file name component, or that escape
|
||||
the extraction directory.
|
||||
@end table
|
||||
|
||||
When @command{tar} extracts archive members from an archive, it strips any
|
||||
@ -9489,9 +9549,10 @@ 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 @file{..} can cause problems when extracting, so
|
||||
File names containing @samp{..} can cause problems when extracting, so
|
||||
@command{tar} normally warns you about such files when creating an
|
||||
archive, and rejects attempts to extracts such files.
|
||||
archive, and prevents attempts to extract such files if that would
|
||||
affect files outside the working directory.
|
||||
|
||||
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
|
||||
@ -9507,52 +9568,17 @@ 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.}
|
||||
|
||||
@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
|
||||
By default @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.
|
||||
|
||||
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
|
||||
However, to suppress this message change to
|
||||
the @file{/} directory first, and then avoid absolute notation.
|
||||
For example:
|
||||
|
||||
@ -9560,8 +9586,15 @@ 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 this option.
|
||||
of using these dangerous options.
|
||||
|
||||
@include parse-datetime.texi
|
||||
|
||||
@ -9596,56 +9629,70 @@ format imposes a number of limitations. The most important of them
|
||||
are:
|
||||
|
||||
@enumerate
|
||||
@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
|
||||
@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
|
||||
devices, fifos etc.)
|
||||
@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
|
||||
@item
|
||||
UIDs and GIDs must be less than @math{2^21} (2,097,152).
|
||||
@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 99
|
||||
characters long will not be able to use @GNUTAR{} @value{VERSION} and
|
||||
however this means that projects containing file names more than 100
|
||||
bytes 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} specification. It stores
|
||||
Archive format defined by @acronym{POSIX.1-1988} and later. It stores
|
||||
symbolic ownership information. It is also able to store
|
||||
special files. However, it imposes several restrictions as well:
|
||||
|
||||
@enumerate
|
||||
@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.
|
||||
@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).
|
||||
@end enumerate
|
||||
|
||||
@item star
|
||||
Format used by J@"org Schilling @command{star}
|
||||
implementation. @GNUTAR{} is able to read @samp{star} archives but
|
||||
currently does not produce them.
|
||||
The format used by the late J@"org Schilling's @command{star}
|
||||
implementation. @GNUTAR{} can read @samp{star} archives but
|
||||
does not produce them.
|
||||
|
||||
@item posix
|
||||
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.
|
||||
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.
|
||||
|
||||
This archive format will be the default format for future versions
|
||||
of @GNUTAR{}.
|
||||
@ -9655,25 +9702,26 @@ of @GNUTAR{}.
|
||||
The following table summarizes the limitations of each of these
|
||||
formats:
|
||||
|
||||
@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
|
||||
@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
|
||||
@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, future version will
|
||||
to create archives in @samp{gnu} format, however, a 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
|
||||
|
||||
@ -10300,6 +10348,7 @@ 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
|
||||
@ -10358,10 +10407,12 @@ When @option{--dereference} (@option{-h}) is used with
|
||||
symbolic links point to, instead of
|
||||
the links themselves.
|
||||
|
||||
When creating portable archives, use @option{--dereference}
|
||||
When creating a portable archive from a directory that adversaries
|
||||
cannot modify, consider using @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
|
||||
@ -10371,7 +10422,8 @@ 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. @xref{Security}.
|
||||
modify directories while @command{tar} is running, or if extracting
|
||||
from an untrusted archive into a nonempty directory. @xref{Security}.
|
||||
|
||||
@node hard links
|
||||
@subsection Hard Links
|
||||
@ -10610,8 +10662,8 @@ will use the following default value:
|
||||
%d/PaxHeaders/%f
|
||||
@end smallexample
|
||||
|
||||
This default is selected to ensure the reproducibility of the
|
||||
archive. @acronym{POSIX} standard recommends to use
|
||||
This default helps make the archive more reproducible.
|
||||
@xref{Reproducibility}. @acronym{POSIX} recommends using
|
||||
@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
|
||||
@ -10712,9 +10764,8 @@ use the following option:
|
||||
|
||||
@cindex archives, binary equivalent
|
||||
@cindex binary equivalent archives, creating
|
||||
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:
|
||||
As another example, the following option helps make the archive
|
||||
more reproducible. @xref{Reproducibility}.
|
||||
|
||||
@smallexample
|
||||
--pax-option delete=atime
|
||||
@ -10800,7 +10851,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 larger than 8GB or with
|
||||
In particular, when trying to archive files 8 GiB or larger, 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
|
||||
@ -10816,7 +10867,9 @@ representations.
|
||||
|
||||
On the other hand, @acronym{POSIX} archives, generally speaking, can
|
||||
be extracted by any tar implementation that understands older
|
||||
@acronym{ustar} format. The only exception are files larger than 8GB.
|
||||
@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}
|
||||
|
||||
@FIXME{Describe how @acronym{POSIX} archives are extracted by non
|
||||
POSIX-aware tars.}
|
||||
@ -10990,17 +11043,12 @@ will deduce the name for the resulting expanded file using the
|
||||
following algorithm:
|
||||
|
||||
@enumerate 1
|
||||
@item If @file{cond-file} does not contain any directories,
|
||||
@file{../cond-file} will be used;
|
||||
@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} 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}}.
|
||||
@item
|
||||
Otherwise, the output file name will be @file{../cond-file}.
|
||||
@end enumerate
|
||||
|
||||
In the unlikely case when this algorithm does not suit your needs,
|
||||
@ -11171,6 +11219,114 @@ 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{}
|
||||
@ -11505,7 +11661,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 current directory. If you want to extract the files in
|
||||
relative to the working 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}
|
||||
@ -11633,10 +11789,8 @@ 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}.) 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.
|
||||
@xref{Standard}.) On some devices, a larger record size can 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
|
||||
@ -12952,28 +13106,35 @@ 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. 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
|
||||
under the working directory. When extracting from an
|
||||
untrusted archive, it is therefore good practice to create an empty
|
||||
directory and run @command{tar} in that directory.
|
||||
directory and run @command{tar} in that directory. You can use the
|
||||
@option{--directory} (@option{-C}) option to specify the working
|
||||
directory (@pxref{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.
|
||||
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.
|
||||
|
||||
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 @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.
|
||||
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.
|
||||
|
||||
Conversely, with the @option{--keep-old-files} (@option{-k}) and
|
||||
@option{--skip-old-files} options, @command{tar} refuses to replace
|
||||
@ -13041,7 +13202,7 @@ Protect archives at least as much as you protect any of the files
|
||||
being archived.
|
||||
|
||||
@item
|
||||
Extract from an untrusted archive only into an otherwise-empty
|
||||
Extract from untrusted archives only into an otherwise-empty
|
||||
directory. This directory and its parent should be accessible only to
|
||||
trusted users. For example:
|
||||
|
||||
@ -13054,8 +13215,6 @@ $ @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.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This is part of GNU tar manual.
|
||||
@c Copyright 1992--2023 Free Software Foundation, Inc.
|
||||
@c Copyright 1992--2026 Free Software Foundation, Inc.
|
||||
@c See file tar.texi for copying conditions.
|
||||
|
||||
@macro GNUTAR
|
||||
|
||||
2
gnulib
2
gnulib
@ -1 +1 @@
|
||||
Subproject commit 46f9c21a08245fe224fd975de8632b04a0256387
|
||||
Subproject commit 60d20df939832119b9281effea68543510448e2f
|
||||
@ -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-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2005-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -18,21 +18,27 @@
|
||||
# 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
|
||||
extern-inline
|
||||
exitfail
|
||||
extern-inline
|
||||
faccessat
|
||||
fchmodat
|
||||
fchownat
|
||||
@ -43,12 +49,13 @@ file-has-acl
|
||||
fileblocks
|
||||
flexmember
|
||||
fnmatch-gnu
|
||||
fprintftime
|
||||
free-posix
|
||||
fseeko
|
||||
fstatat
|
||||
full-read
|
||||
full-write
|
||||
futimens
|
||||
gendocs
|
||||
getline
|
||||
getopt-gnu
|
||||
getpagesize
|
||||
@ -57,53 +64,66 @@ gettime
|
||||
gitlog-to-changelog
|
||||
hash
|
||||
human
|
||||
ialloc
|
||||
idx
|
||||
intprops
|
||||
inttostr
|
||||
inttypes
|
||||
inttypes-h
|
||||
issymlinkat
|
||||
largefile
|
||||
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
|
||||
full-read
|
||||
safe-read
|
||||
same-inode
|
||||
savedir
|
||||
selinux-at
|
||||
setenv
|
||||
snprintf
|
||||
stat-time
|
||||
stdbool
|
||||
stdint
|
||||
stpcpy
|
||||
std-gnu23
|
||||
stdcountof-h
|
||||
stddef-h
|
||||
stdint-h
|
||||
stdopen
|
||||
stpcpy
|
||||
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
1
lib/.gitignore
vendored
@ -6,5 +6,4 @@ paxnames.c
|
||||
rmt-command.h
|
||||
rmt.h
|
||||
rtapelib.c
|
||||
system-ioctl.h
|
||||
system.h
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar library. -*- Makefile -*-
|
||||
|
||||
# Copyright 1994-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -33,7 +33,6 @@ noinst_HEADERS = \
|
||||
paxlib.h\
|
||||
rmt.h\
|
||||
system.h\
|
||||
system-ioctl.h\
|
||||
wordsplit.h\
|
||||
xattr-at.h
|
||||
|
||||
@ -41,7 +40,7 @@ libtar_a_SOURCES = \
|
||||
paxerror.c paxexit-status.c paxlib.h paxnames.c \
|
||||
rtapelib.c \
|
||||
rmt.h \
|
||||
system.h system-ioctl.h \
|
||||
system.h \
|
||||
wordsplit.c\
|
||||
xattr-at.c
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* Replacement <attr/xattr.h> for platforms that lack it.
|
||||
Copyright 2012-2023 Free Software Foundation, Inc.
|
||||
Copyright 2012-2026 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
|
||||
|
||||
839
lib/wordsplit.c
839
lib/wordsplit.c
File diff suppressed because it is too large
Load Diff
@ -18,9 +18,15 @@
|
||||
#define __WORDSPLIT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <attribute.h>
|
||||
#include <idx.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]
|
||||
@ -36,42 +42,42 @@ typedef struct wordsplit wordsplit_t;
|
||||
default value upon entry to wordsplit(). */
|
||||
struct wordsplit
|
||||
{
|
||||
size_t ws_wordc; /* [Output] Number of words in ws_wordv. */
|
||||
idx_t ws_wordc; /* [Output] Number of words in ws_wordv. */
|
||||
char **ws_wordv; /* [Output] Array of parsed out words. */
|
||||
size_t ws_offs; /* [Input] (WRDSF_DOOFFS) Number of initial
|
||||
idx_t ws_offs; /* [Input] (WRDSF_DOOFFS) Number of initial
|
||||
elements in ws_wordv to fill with NULLs. */
|
||||
size_t ws_wordn; /* Number of elements ws_wordv can accommodate. */
|
||||
idx_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. */
|
||||
size_t ws_maxwords; /* [Input] (WRDSO_MAXWORDS) Return at most that
|
||||
idx_t ws_maxwords; /* [Input] (WRDSO_MAXWORDS) Return at most that
|
||||
many words */
|
||||
size_t ws_wordi; /* [Output] (WRDSF_INCREMENTAL) Total number of
|
||||
idx_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) (wordsplit_t *wsp);
|
||||
void (*ws_alloc_die) (struct wordsplit *wsp);
|
||||
/* [Input] (WRDSF_ALLOC_DIE) Function called when
|
||||
out of memory. Must not return. */
|
||||
void (*ws_error) (const char *, ...)
|
||||
ATTRIBUTE_FORMAT ((printf, 1, 2));
|
||||
_GL_ATTRIBUTE_FORMAT ((printf, 1, 2));
|
||||
/* [Input] (WRDSF_ERROR) Function used for error
|
||||
reporting */
|
||||
void (*ws_debug) (const char *, ...)
|
||||
ATTRIBUTE_FORMAT ((printf, 1, 2));
|
||||
_GL_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;
|
||||
size_t ws_envidx;
|
||||
size_t ws_envsiz;
|
||||
idx_t ws_envidx;
|
||||
idx_t ws_envsiz;
|
||||
|
||||
int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
|
||||
int (*ws_getvar) (char **ret, const char *var, idx_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
|
||||
@ -85,7 +91,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, size_t len, char **argv,
|
||||
int (*ws_command) (char **ret, const char *cmd, idx_t len, char **argv,
|
||||
void *clos);
|
||||
/* [Input] (!WRDSF_NOCMD) Returns in the memory
|
||||
location pointed to by RET the expansion of
|
||||
@ -97,21 +103,19 @@ struct wordsplit
|
||||
return values. */
|
||||
|
||||
const char *ws_input; /* Input string (the S argument to wordsplit. */
|
||||
size_t ws_len; /* Length of ws_input. */
|
||||
size_t ws_endp; /* Points past the last processed byte in
|
||||
idx_t ws_len; /* Length of ws_input. */
|
||||
idx_t ws_endp; /* Points past the last processed byte in
|
||||
ws_input. */
|
||||
int ws_errno; /* [Output] Error code, if an error occurred. */
|
||||
int ws_errno; /* [Output] Error code, if an error occurred.
|
||||
This is not the same as a POSIX errno value. */
|
||||
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. */
|
||||
int ws_lvl; /* Invocation nesting level. */
|
||||
idx_t 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. */
|
||||
@ -224,11 +228,7 @@ struct wordsplit
|
||||
#define WRDSX_WORD 0
|
||||
#define WRDSX_QUOTE 1
|
||||
|
||||
/* 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)))
|
||||
|
||||
/* Error codes. */
|
||||
#define WRDSE_OK 0
|
||||
#define WRDSE_EOF WRDSE_OK
|
||||
#define WRDSE_QUOTE 1
|
||||
@ -241,23 +241,26 @@ struct wordsplit
|
||||
#define WRDSE_GLOBERR 8
|
||||
#define WRDSE_USERERR 9
|
||||
|
||||
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);
|
||||
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);
|
||||
void wordsplit_free_words (wordsplit_t *ws);
|
||||
void wordsplit_free_envbuf (wordsplit_t *ws);
|
||||
int wordsplit_get_words (wordsplit_t *ws, size_t *wordc, char ***wordv);
|
||||
void wordsplit_get_words (wordsplit_t *ws, idx_t *wordc, char ***wordv);
|
||||
|
||||
int wordsplit_append (wordsplit_t *wsp, int argc, char **argv);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
void wordsplit_perror (wordsplit_t *ws);
|
||||
const char *wordsplit_strerror (wordsplit_t *ws);
|
||||
|
||||
void wordsplit_clearerr (wordsplit_t *ws);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* openat-style fd-relative functions for operating with extended file
|
||||
attributes.
|
||||
|
||||
Copyright 2012-2023 Free Software Foundation, Inc.
|
||||
Copyright 2012-2026 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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* Prototypes for openat-style fd-relative functions for operating with
|
||||
extended file attributes.
|
||||
|
||||
Copyright 2012-2023 Free Software Foundation, Inc.
|
||||
Copyright 2012-2026 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 idenfified by NAME and associated with the given PATH in the
|
||||
attribute identified 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,
|
||||
|
||||
2
paxutils
2
paxutils
@ -1 +1 @@
|
||||
Subproject commit 481bae11050fcbdca67a66eb57390267b280a312
|
||||
Subproject commit bb78da089e1086c9403a29d838231f50e9ff25c4
|
||||
@ -1,6 +1,6 @@
|
||||
# List of files which contain translatable strings.
|
||||
|
||||
# Copyright 1996-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1996-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Make GNU tar scripts.
|
||||
|
||||
# Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ SLEEP_MESSAGE="`awk '
|
||||
}' /dev/null`"
|
||||
|
||||
|
||||
# Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Make backups.
|
||||
|
||||
# Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -342,7 +342,7 @@ remote_run() {
|
||||
|
||||
license() {
|
||||
cat - <<EOF
|
||||
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
Copyright (C) 2013, 2025-2026 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.
|
||||
|
||||
@ -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-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Restore backups.
|
||||
|
||||
# Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#! /usr/bin/perl -w
|
||||
# Display and edit the 'dev' field in tar's snapshots
|
||||
# Copyright 2007-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2007-2026 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.)
|
||||
|
||||
|
||||
@ -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-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* xsparse - expands compressed sparse file images extracted from GNU tar
|
||||
archives.
|
||||
|
||||
Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -20,16 +20,17 @@
|
||||
|
||||
Written by Sergey Poznyakoff */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#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 <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Bound on length of the string representing an off_t.
|
||||
See INT_STRLEN_BOUND in intprops.h for explanation */
|
||||
@ -44,10 +45,10 @@ struct sp_array
|
||||
off_t numbytes;
|
||||
};
|
||||
|
||||
char *progname;
|
||||
int verbose;
|
||||
static char *progname;
|
||||
static bool verbose;
|
||||
|
||||
void
|
||||
static void
|
||||
die (int code, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@ -60,58 +61,42 @@ die (int code, char *fmt, ...)
|
||||
exit (code);
|
||||
}
|
||||
|
||||
void *
|
||||
static void *
|
||||
emalloc (size_t size)
|
||||
{
|
||||
char *p = malloc (size);
|
||||
if (!p)
|
||||
if (!p && size)
|
||||
die (1, "not enough memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
off_t
|
||||
static off_t
|
||||
string_to_off (char *p, char **endp)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
return v;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_to_size (char *p, char **endp)
|
||||
static size_t
|
||||
string_to_size (char *p, char **endp, size_t maxsize)
|
||||
{
|
||||
off_t v = string_to_off (p, endp);
|
||||
size_t ret = v;
|
||||
if (ret != v)
|
||||
if (! (ret == v && ret <= maxsize))
|
||||
die (1, "number too big");
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t sparse_map_size;
|
||||
struct sp_array *sparse_map;
|
||||
static size_t sparse_map_size;
|
||||
static struct sp_array *sparse_map;
|
||||
|
||||
void
|
||||
static void
|
||||
get_line (char *s, int size, FILE *stream)
|
||||
{
|
||||
char *p = fgets (s, size, stream);
|
||||
@ -121,11 +106,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, "buffer overflow");
|
||||
s[len - 1] = 0;
|
||||
die (1, "invalid or too-long data");
|
||||
s[len - 1] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
static bool
|
||||
get_var (FILE *fp, char **name, char **value)
|
||||
{
|
||||
static char *buffer;
|
||||
@ -138,12 +123,12 @@ get_var (FILE *fp, char **name, char **value)
|
||||
size_t len, s;
|
||||
|
||||
if (!fgets (buffer, bufsize, fp))
|
||||
return 0;
|
||||
return false;
|
||||
len = strlen (buffer);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
s = string_to_size (buffer, &p);
|
||||
s = string_to_size (buffer, &p, SIZE_MAX - 1);
|
||||
if (*p != ' ')
|
||||
die (1, "malformed header: expected space but found %s", p);
|
||||
if (buffer[len-1] != '\n')
|
||||
@ -160,7 +145,7 @@ get_var (FILE *fp, char **name, char **value)
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (memcmp (p, "GNU.sparse.", 11));
|
||||
while (strncmp (p, "GNU.sparse.", 11) != 0);
|
||||
|
||||
p += 11;
|
||||
q = strchr (p, '=');
|
||||
@ -170,20 +155,19 @@ get_var (FILE *fp, char **name, char **value)
|
||||
q[strlen (q) - 1] = 0;
|
||||
*name = p;
|
||||
*value = q;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
char *outname;
|
||||
off_t outsize;
|
||||
unsigned version_major;
|
||||
unsigned version_minor;
|
||||
static char *outname;
|
||||
static off_t outsize;
|
||||
static off_t version_major;
|
||||
static off_t version_minor;
|
||||
|
||||
void
|
||||
static void
|
||||
read_xheader (char *name)
|
||||
{
|
||||
char *kw, *val;
|
||||
FILE *fp = fopen (name, "r");
|
||||
char *expect = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
if (verbose)
|
||||
@ -194,10 +178,6 @@ 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);
|
||||
@ -205,11 +185,11 @@ read_xheader (char *name)
|
||||
}
|
||||
else if (strcmp (kw, "major") == 0)
|
||||
{
|
||||
version_major = string_to_size (val, NULL);
|
||||
version_major = string_to_off (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "minor") == 0)
|
||||
{
|
||||
version_minor = string_to_size (val, NULL);
|
||||
version_minor = string_to_off (val, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "realsize") == 0
|
||||
|| strcmp (kw, "size") == 0)
|
||||
@ -218,17 +198,27 @@ read_xheader (char *name)
|
||||
}
|
||||
else if (strcmp (kw, "numblocks") == 0)
|
||||
{
|
||||
sparse_map_size = string_to_size (val, NULL);
|
||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -252,16 +242,15 @@ 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");
|
||||
fclose (fp);
|
||||
if (ferror (fp) || fclose (fp) < 0)
|
||||
die (1, "read error: %s", name);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
read_map (FILE *ifp)
|
||||
{
|
||||
size_t i;
|
||||
@ -271,7 +260,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);
|
||||
sparse_map_size = string_to_size (nbuf, NULL, SIZE_MAX / sizeof *sparse_map);
|
||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||
|
||||
for (i = 0; i < sparse_map_size; i++)
|
||||
@ -282,52 +271,51 @@ read_map (FILE *ifp)
|
||||
sparse_map[i].numbytes = string_to_off (nbuf, NULL);
|
||||
}
|
||||
|
||||
fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
|
||||
SEEK_SET);
|
||||
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");
|
||||
}
|
||||
|
||||
void
|
||||
static 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)
|
||||
ftruncate (ofd, sparse_map[i].offset);
|
||||
{
|
||||
if (0 <= ofd && ftruncate (ofd, sparse_map[i].offset) < 0)
|
||||
die (1, "ftruncate error (%d)", errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
lseek (ofd, sparse_map[i].offset, SEEK_SET);
|
||||
if (0 <= ofd && lseek (ofd, sparse_map[i].offset, SEEK_SET) < 0)
|
||||
die (1, "lseek error (%d)", errno);
|
||||
while (size)
|
||||
{
|
||||
size_t rdsize = (size < maxbytes) ? size : maxbytes;
|
||||
char buffer[BUFSIZ];
|
||||
size_t rdsize = size < BUFSIZ ? size : BUFSIZ;
|
||||
if (rdsize != fread (buffer, 1, rdsize, sfp))
|
||||
die (1, "read error (%d)", errno);
|
||||
if (rdsize != write (ofd, buffer, rdsize))
|
||||
die (1, "write 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);
|
||||
}
|
||||
size -= rdsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
free (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
usage (int code)
|
||||
{
|
||||
printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
|
||||
@ -342,58 +330,26 @@ usage (int code)
|
||||
exit (code);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
guess_outname (char *name)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
int dry_run = 0;
|
||||
bool dry_run = false;
|
||||
char *xheader_file = NULL;
|
||||
char *inname;
|
||||
FILE *ifp;
|
||||
struct stat st;
|
||||
int ofd;
|
||||
|
||||
progname = argv[0];
|
||||
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
|
||||
@ -409,9 +365,9 @@ main (int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
dry_run = 1;
|
||||
dry_run = true;
|
||||
case 'v':
|
||||
verbose++;
|
||||
verbose = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -428,15 +384,16 @@ main (int argc, char **argv)
|
||||
if (xheader_file)
|
||||
read_xheader (xheader_file);
|
||||
|
||||
inname = argv[0];
|
||||
char *inname = argv[0];
|
||||
if (argv[1])
|
||||
outname = argv[1];
|
||||
|
||||
if (stat (inname, &st))
|
||||
struct stat st;
|
||||
if (stat (inname, &st) < 0)
|
||||
die (1, "cannot stat %s (%d)", inname, errno);
|
||||
|
||||
ifp = fopen (inname, "r");
|
||||
if (ifp == NULL)
|
||||
FILE *ifp = fopen (inname, "r");
|
||||
if (!ifp)
|
||||
die (1, "cannot open file %s (%d)", inname, errno);
|
||||
|
||||
if (!xheader_file || version_major == 1)
|
||||
@ -445,30 +402,36 @@ 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))
|
||||
if (stat (outname, &st) < 0)
|
||||
die (1, "cannot stat output file %s (%d)", outname, errno);
|
||||
if (st.st_size != outsize)
|
||||
die (1, "expanded file has wrong size");
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar sources.
|
||||
|
||||
# Copyright 1994-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -50,6 +50,7 @@ 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) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)\
|
||||
$(LIB_GETRANDOM) $(LIB_HARD_LOCALE) $(FILE_HAS_ACL_LIB) $(LIB_MBRTOWC)\
|
||||
$(LIB_SELINUX) $(LIB_SETLOCALE_NULL)
|
||||
$(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)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* Long integers, for GNU tar.
|
||||
Copyright 1999-2023 Free Software Foundation, Inc.
|
||||
Copyright 1999-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
712
src/buffer.c
712
src/buffer.c
File diff suppressed because it is too large
Load Diff
265
src/checkpoint.c
265
src/checkpoint.c
@ -1,6 +1,6 @@
|
||||
/* Checkpoint management for tar.
|
||||
|
||||
Copyright 2007-2023 Free Software Foundation, Inc.
|
||||
Copyright 2007-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -19,10 +19,13 @@
|
||||
|
||||
#include <system.h>
|
||||
#include "common.h"
|
||||
#include "wordsplit.h"
|
||||
|
||||
#include <wordsplit.h>
|
||||
#include <flexmember.h>
|
||||
#include <strftime.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include "fprintftime.h"
|
||||
#include <signal.h>
|
||||
|
||||
enum checkpoint_opcode
|
||||
@ -47,103 +50,83 @@ struct checkpoint_action
|
||||
char *command;
|
||||
int signal;
|
||||
} v;
|
||||
char commandbuf[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
|
||||
/* Checkpointing counter */
|
||||
static unsigned checkpoint;
|
||||
static intmax_t checkpoint;
|
||||
|
||||
/* List of checkpoint actions */
|
||||
static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
|
||||
static struct checkpoint_action *checkpoint_action,
|
||||
**checkpoint_action_tail = &checkpoint_action;
|
||||
|
||||
/* State of the checkpoint system */
|
||||
enum {
|
||||
static enum {
|
||||
CHKP_INIT, /* Needs initialization */
|
||||
CHKP_COMPILE, /* Actions are being compiled */
|
||||
CHKP_RUN /* Actions are being run */
|
||||
};
|
||||
static int checkpoint_state;
|
||||
} checkpoint_state;
|
||||
/* Blocked signals */
|
||||
static sigset_t sigs;
|
||||
|
||||
static struct checkpoint_action *
|
||||
alloc_action (enum checkpoint_opcode opcode)
|
||||
alloc_action (enum checkpoint_opcode opcode, char const *quoted_string)
|
||||
{
|
||||
struct checkpoint_action *p = xzalloc (sizeof *p);
|
||||
if (checkpoint_action_tail)
|
||||
checkpoint_action_tail->next = p;
|
||||
else
|
||||
checkpoint_action = p;
|
||||
checkpoint_action_tail = p;
|
||||
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;
|
||||
p->opcode = opcode;
|
||||
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)
|
||||
if (quoted_string)
|
||||
{
|
||||
memmove (output, output+1, len-2);
|
||||
output[len-2] = 0;
|
||||
p->v.command = memcpy (p->commandbuf, quoted_string, quoted_size);
|
||||
unquote_string (p->v.command);
|
||||
}
|
||||
unquote_string (output);
|
||||
return output;
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
checkpoint_compile_action (const char *str)
|
||||
{
|
||||
struct checkpoint_action *act;
|
||||
|
||||
if (checkpoint_state == CHKP_INIT)
|
||||
{
|
||||
sigemptyset (&sigs);
|
||||
checkpoint_state = CHKP_COMPILE;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
else if (strncmp (str, "echo=", 5) == 0)
|
||||
{
|
||||
act = alloc_action (cop_echo);
|
||||
act->v.command = copy_string_unquote (str + 5);
|
||||
}
|
||||
alloc_action (cop_echo, str + 5);
|
||||
else if (strncmp (str, "exec=", 5) == 0)
|
||||
{
|
||||
act = alloc_action (cop_exec);
|
||||
act->v.command = copy_string_unquote (str + 5);
|
||||
}
|
||||
alloc_action (cop_exec, str + 5);
|
||||
else if (strncmp (str, "ttyout=", 7) == 0)
|
||||
{
|
||||
act = alloc_action (cop_ttyout);
|
||||
act->v.command = copy_string_unquote (str + 7);
|
||||
}
|
||||
alloc_action (cop_ttyout, str + 7);
|
||||
else if (strncmp (str, "sleep=", 6) == 0)
|
||||
{
|
||||
char const *arg = str + 6;
|
||||
char *p;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
else if (strcmp (str, "totals") == 0)
|
||||
alloc_action (cop_totals);
|
||||
else if (streq (str, "totals"))
|
||||
alloc_action (cop_totals, NULL);
|
||||
else if (strncmp (str, "wait=", 5) == 0)
|
||||
{
|
||||
act = alloc_action (cop_wait);
|
||||
act->v.signal = decode_signal (str + 5);
|
||||
sigaddset (&sigs, act->v.signal);
|
||||
int sig = decode_signal (str + 5);
|
||||
alloc_action (cop_wait, NULL)->v.signal = sig;
|
||||
sigaddset (&sigs, sig);
|
||||
}
|
||||
else
|
||||
FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
|
||||
paxfatal (0, _("%s: unknown checkpoint action"), str);
|
||||
}
|
||||
|
||||
void
|
||||
@ -169,13 +152,10 @@ checkpoint_finish_compile (void)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *checkpoint_total_format[] = {
|
||||
"R",
|
||||
"W",
|
||||
"D"
|
||||
};
|
||||
|
||||
static long
|
||||
/* 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
|
||||
getwidth (FILE *fp)
|
||||
{
|
||||
char const *columns;
|
||||
@ -189,8 +169,9 @@ getwidth (FILE *fp)
|
||||
columns = getenv ("COLUMNS");
|
||||
if (columns)
|
||||
{
|
||||
long int col = strtol (columns, NULL, 10);
|
||||
if (0 < col)
|
||||
char *end;
|
||||
intmax_t col = stoint (columns, &end, NULL, 0, INTMAX_MAX);
|
||||
if (! (*end | !col))
|
||||
return col;
|
||||
}
|
||||
|
||||
@ -198,24 +179,20 @@ getwidth (FILE *fp)
|
||||
}
|
||||
|
||||
static char *
|
||||
getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
|
||||
getarg (char const *input, char const **endp, char **argbuf, idx_t *arglen)
|
||||
{
|
||||
if (input[0] == '{')
|
||||
{
|
||||
char *p = strchr (input + 1, '}');
|
||||
char const *p = strchr (input + 1, '}');
|
||||
if (p)
|
||||
{
|
||||
size_t n = p - input;
|
||||
idx_t n = p - input;
|
||||
if (n > *arglen)
|
||||
{
|
||||
*arglen = n;
|
||||
*argbuf = xrealloc (*argbuf, *arglen);
|
||||
}
|
||||
*argbuf = xpalloc (*argbuf, arglen, n - *arglen, -1, 1);
|
||||
n--;
|
||||
memcpy (*argbuf, input + 1, n);
|
||||
(*argbuf)[n] = 0;
|
||||
*endp = p + 1;
|
||||
return *argbuf;
|
||||
(*argbuf)[n] = 0;
|
||||
return memcpy (*argbuf, input + 1, n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,38 +200,32 @@ getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int tty_cleanup;
|
||||
static bool tty_cleanup;
|
||||
|
||||
static const char *def_format =
|
||||
"%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
|
||||
|
||||
static int
|
||||
format_checkpoint_string (FILE *fp, size_t len,
|
||||
static intmax_t
|
||||
format_checkpoint_string (FILE *fp, intmax_t len,
|
||||
const char *input, bool do_write,
|
||||
unsigned cpn)
|
||||
intmax_t 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 size_t arglen = 0;
|
||||
static idx_t arglen = 0;
|
||||
char *arg = NULL;
|
||||
|
||||
if (!input)
|
||||
{
|
||||
if (do_write)
|
||||
/* TRANSLATORS: This is a "checkpoint of write operation",
|
||||
*not* "Writing a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de escritura",
|
||||
*not* "Escribiendo un punto de comprobaci@'on" */
|
||||
*not* "Writing a checkpoint". */
|
||||
input = gettext ("Write checkpoint %u");
|
||||
else
|
||||
/* TRANSLATORS: This is a "checkpoint of read operation",
|
||||
*not* "Reading a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de lectura",
|
||||
*not* "Leyendo un punto de comprobaci@'on" */
|
||||
*not* "Reading a checkpoint". */
|
||||
input = gettext ("Read checkpoint %u");
|
||||
}
|
||||
|
||||
@ -269,44 +240,52 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
{
|
||||
fputc ('%', fp);
|
||||
fputc (*ip, fp);
|
||||
len += 2;
|
||||
len = add_printf (len, 2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (*ip)
|
||||
{
|
||||
case 'c':
|
||||
len += format_checkpoint_string (fp, len, def_format, do_write,
|
||||
cpn);
|
||||
len = add_printf (len,
|
||||
format_checkpoint_string (fp, len, def_format,
|
||||
do_write, cpn));
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
fputs (cps, fp);
|
||||
len += strlen (cps);
|
||||
len = add_printf (len, fprintf (fp, "%jd", cpn));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
fputs (opstr, fp);
|
||||
len += strlen (opstr);
|
||||
len = add_printf (len, strlen (opstr));
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
len += fprintf (fp, "%.0f", compute_duration ());
|
||||
len = add_printf (len,
|
||||
fprintf (fp, "%.0f",
|
||||
compute_duration_ns () / BILLION));
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
{
|
||||
const char **fmt = checkpoint_total_format, *fmtbuf[3];
|
||||
static char const *const checkpoint_total_format[]
|
||||
= { "R", "W", "D" };
|
||||
char const *const *fmt = checkpoint_total_format, *fmtbuf[3];
|
||||
struct wordsplit ws;
|
||||
compute_duration ();
|
||||
compute_duration_ns ();
|
||||
|
||||
if (arg)
|
||||
{
|
||||
ws.ws_delim = ",";
|
||||
if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
|
||||
WRDSF_QUOTE | WRDSF_DELIM))
|
||||
ERROR ((0, 0, _("cannot split string '%s': %s"),
|
||||
arg, wordsplit_strerror (&ws)));
|
||||
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);
|
||||
else
|
||||
{
|
||||
int i;
|
||||
@ -318,7 +297,7 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
fmt = fmtbuf;
|
||||
}
|
||||
}
|
||||
len += format_total_stats (fp, fmt, ',', 0);
|
||||
len = add_printf (len, format_total_stats (fp, fmt, ',', 0));
|
||||
if (arg)
|
||||
wordsplit_free (&ws);
|
||||
}
|
||||
@ -326,28 +305,68 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
|
||||
case 't':
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
const char *fmt = arg ? arg : "%c";
|
||||
struct timespec ts = current_timespec ();
|
||||
struct tm *tm = localtime (&ts.tv_sec);
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
tm = localtime (&tv.tv_sec);
|
||||
len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
|
||||
#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));
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
{
|
||||
long w = arg ? strtol (arg, NULL, 10) : getwidth (fp);
|
||||
for (; w > len; len++)
|
||||
fputc (' ', fp);
|
||||
}
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fputc ('%', fp);
|
||||
fputc (*ip, fp);
|
||||
len += 2;
|
||||
len = add_printf (len, 2);
|
||||
break;
|
||||
}
|
||||
arg = NULL;
|
||||
@ -358,10 +377,10 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
if (*ip == '\r')
|
||||
{
|
||||
len = 0;
|
||||
tty_cleanup = 1;
|
||||
tty_cleanup = true;
|
||||
}
|
||||
else
|
||||
len++;
|
||||
len = add_printf (len, 1);
|
||||
}
|
||||
}
|
||||
fflush (fp);
|
||||
@ -422,7 +441,7 @@ run_checkpoint_actions (bool do_write)
|
||||
break;
|
||||
|
||||
case cop_totals:
|
||||
compute_duration ();
|
||||
compute_duration_ns ();
|
||||
print_total_stats ();
|
||||
break;
|
||||
|
||||
@ -447,7 +466,7 @@ checkpoint_flush_actions (void)
|
||||
case cop_ttyout:
|
||||
if (tty && tty_cleanup)
|
||||
{
|
||||
long w = getwidth (tty);
|
||||
intmax_t w = getwidth (tty);
|
||||
while (w--)
|
||||
fputc (' ', tty);
|
||||
fputc ('\r', tty);
|
||||
|
||||
669
src/common.h
669
src/common.h
File diff suppressed because it is too large
Load Diff
219
src/compare.c
219
src/compare.c
@ -1,6 +1,6 @@
|
||||
/* Diff files from a tar archive.
|
||||
|
||||
Copyright 1988-2023 Free Software Foundation, Inc.
|
||||
Copyright 1988-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -20,15 +20,21 @@
|
||||
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. */
|
||||
@ -44,8 +50,7 @@ static char *diff_buffer;
|
||||
void
|
||||
diff_init (void)
|
||||
{
|
||||
void *ptr;
|
||||
diff_buffer = page_aligned_alloc (&ptr, record_size);
|
||||
diff_buffer = xalignalloc (getpagesize (), record_size);
|
||||
if (listed_incremental_option)
|
||||
read_directory_file ();
|
||||
}
|
||||
@ -72,20 +77,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 int
|
||||
process_noop (MAYBE_UNUSED size_t size, MAYBE_UNUSED char *data)
|
||||
static bool
|
||||
process_noop (MAYBE_UNUSED idx_t size, MAYBE_UNUSED char *data)
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
process_rawdata (size_t bytes, char *buffer)
|
||||
static bool
|
||||
process_rawdata (idx_t bytes, char *buffer)
|
||||
{
|
||||
size_t status = blocking_read (diff_handle, diff_buffer, bytes);
|
||||
idx_t status = blocking_read (diff_handle, diff_buffer, bytes);
|
||||
|
||||
if (status != bytes)
|
||||
if (status < bytes)
|
||||
{
|
||||
if (status == SAFE_READ_ERROR)
|
||||
if (errno)
|
||||
{
|
||||
read_error (current_stat_info.file_name);
|
||||
report_difference (¤t_stat_info, NULL);
|
||||
@ -93,53 +98,48 @@ process_rawdata (size_t bytes, char *buffer)
|
||||
else
|
||||
{
|
||||
report_difference (¤t_stat_info,
|
||||
ngettext ("Could only read %lu of %lu byte",
|
||||
"Could only read %lu of %lu bytes",
|
||||
ngettext ("Could read only %td of %td byte",
|
||||
"Could read only %td of %td bytes",
|
||||
bytes),
|
||||
(unsigned long) status, (unsigned long) bytes);
|
||||
status, bytes);
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp (buffer, diff_buffer, bytes))
|
||||
if (!memeq (buffer, diff_buffer, bytes))
|
||||
{
|
||||
report_difference (¤t_stat_info, _("Contents differ"));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
/* 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. */
|
||||
|
||||
static void
|
||||
read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
|
||||
read_and_process (struct tar_stat_info *st, bool (*processor) (idx_t, char *))
|
||||
{
|
||||
union block *data_block;
|
||||
size_t data_size;
|
||||
off_t size = st->stat.st_size;
|
||||
|
||||
mv_begin_read (st);
|
||||
while (size)
|
||||
for (off_t size = st->stat.st_size; size; )
|
||||
{
|
||||
data_block = find_next_block ();
|
||||
union block *data_block = find_next_block ();
|
||||
if (! data_block)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxerror (0, _("Unexpected EOF in archive"));
|
||||
return;
|
||||
}
|
||||
|
||||
data_size = available_space_after (data_block);
|
||||
idx_t data_size = available_space_after (data_block);
|
||||
if (data_size > size)
|
||||
data_size = size;
|
||||
if (!(*processor) (data_size, data_block->buffer))
|
||||
if (!processor (data_size, charptr (data_block)))
|
||||
processor = process_noop;
|
||||
set_next_block_after ((union block *)
|
||||
(data_block->buffer + data_size - 1));
|
||||
set_next_block_after (charptr (data_block) + data_size - 1);
|
||||
size -= data_size;
|
||||
mv_size_left (size);
|
||||
}
|
||||
@ -148,23 +148,18 @@ read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
|
||||
|
||||
/* Call either stat or lstat over STAT_DATA, depending on
|
||||
--dereference (-h), for a file which should exist. Diagnose any
|
||||
problem. Return nonzero for success, zero otherwise. */
|
||||
static int
|
||||
problem. Return true for success, false otherwise. */
|
||||
static bool
|
||||
get_stat_data (char const *file_name, struct stat *stat_data)
|
||||
{
|
||||
int status = deref_stat (file_name, stat_data);
|
||||
|
||||
if (status != 0)
|
||||
if (deref_stat (file_name, stat_data) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
stat_warn (file_name);
|
||||
else
|
||||
stat_error (file_name);
|
||||
(errno == ENOENT ? stat_warn : stat_error) (file_name);
|
||||
report_difference (¤t_stat_info, NULL);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -218,7 +213,9 @@ diff_file (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
diff_handle = openat (chdir_fd, file_name, open_read_flags);
|
||||
struct fdbase f = fdbase (file_name);
|
||||
diff_handle = (f.fd == BADFD ? -1
|
||||
: openat (f.fd, f.base, open_read_flags));
|
||||
|
||||
if (diff_handle < 0)
|
||||
{
|
||||
@ -228,8 +225,6 @@ diff_file (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
int status;
|
||||
|
||||
if (current_stat_info.is_sparse)
|
||||
sparse_diff_file (diff_handle, ¤t_stat_info);
|
||||
else
|
||||
@ -239,13 +234,11 @@ diff_file (void)
|
||||
&& stat_data.st_size != 0)
|
||||
{
|
||||
struct timespec atime = get_stat_atime (&stat_data);
|
||||
if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
|
||||
!= 0)
|
||||
if (set_file_atime (diff_handle, f.fd, f.base, atime) < 0)
|
||||
utime_error (file_name);
|
||||
}
|
||||
|
||||
status = close (diff_handle);
|
||||
if (status != 0)
|
||||
if (close (diff_handle) < 0)
|
||||
close_error (file_name);
|
||||
}
|
||||
}
|
||||
@ -260,23 +253,22 @@ diff_link (void)
|
||||
|
||||
if (get_stat_data (current_stat_info.file_name, &file_data)
|
||||
&& get_stat_data (current_stat_info.link_name, &link_data)
|
||||
&& !sys_compare_links (&file_data, &link_data))
|
||||
&& !psame_inode (&file_data, &link_data))
|
||||
report_difference (¤t_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];
|
||||
size_t len = strlen (current_stat_info.link_name);
|
||||
idx_t len = strlen (current_stat_info.link_name);
|
||||
char *linkbuf = len < sizeof buf ? buf : xmalloc (len + 1);
|
||||
|
||||
ssize_t status = readlinkat (chdir_fd, current_stat_info.file_name,
|
||||
linkbuf, 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));
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
@ -287,13 +279,12 @@ diff_symlink (void)
|
||||
report_difference (¤t_stat_info, NULL);
|
||||
}
|
||||
else if (status != len
|
||||
|| memcmp (current_stat_info.link_name, linkbuf, len) != 0)
|
||||
|| !memeq (current_stat_info.link_name, linkbuf, len))
|
||||
report_difference (¤t_stat_info, _("Symlink differs"));
|
||||
|
||||
if (linkbuf != buf)
|
||||
free (linkbuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
diff_special (void)
|
||||
@ -329,37 +320,42 @@ diff_special (void)
|
||||
report_difference (¤t_stat_info, _("Mode differs"));
|
||||
}
|
||||
|
||||
static int
|
||||
/* Return zero if and only if A and B should be considered equal.
|
||||
for the purposes of dump directory comparison. */
|
||||
static char
|
||||
dumpdir_cmp (const char *a, const char *b)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
while (*a)
|
||||
switch (*a)
|
||||
{
|
||||
case 'Y':
|
||||
case 'N':
|
||||
if (!strchr ("YN", *b))
|
||||
/* 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'))
|
||||
return 1;
|
||||
if (strcmp(a + 1, b + 1))
|
||||
return 1;
|
||||
len = strlen (a) + 1;
|
||||
a += len;
|
||||
b += len;
|
||||
break;
|
||||
|
||||
a++, b++;
|
||||
FALLTHROUGH;
|
||||
case 'D':
|
||||
if (strcmp(a, b))
|
||||
/* 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)
|
||||
return 1;
|
||||
len = strlen (a) + 1;
|
||||
a += len;
|
||||
b += len;
|
||||
a++, b++;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
case 'T':
|
||||
case 'X':
|
||||
return *b;
|
||||
|
||||
default:
|
||||
unreachable ();
|
||||
}
|
||||
return *b;
|
||||
}
|
||||
@ -375,7 +371,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))
|
||||
else if (fstat (fd, &dir->stat) < 0)
|
||||
{
|
||||
diag = stat_diag;
|
||||
close (fd);
|
||||
@ -392,7 +388,7 @@ diff_dumpdir (struct tar_stat_info *dir)
|
||||
|
||||
if (dumpdir_buffer)
|
||||
{
|
||||
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer))
|
||||
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer) != 0)
|
||||
report_difference (dir, _("Contents differ"));
|
||||
}
|
||||
else
|
||||
@ -402,16 +398,13 @@ 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;
|
||||
|
||||
@ -422,10 +415,11 @@ diff_multivol (void)
|
||||
return;
|
||||
}
|
||||
|
||||
offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
|
||||
off_t offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
|
||||
off_t file_size;
|
||||
if (offset < 0
|
||||
|| INT_ADD_OVERFLOW (current_stat_info.stat.st_size, offset)
|
||||
|| stat_data.st_size != current_stat_info.stat.st_size + offset)
|
||||
|| ckd_add (&file_size, current_stat_info.stat.st_size, offset)
|
||||
|| stat_data.st_size != file_size)
|
||||
{
|
||||
report_difference (¤t_stat_info, _("Size differs"));
|
||||
skip_member ();
|
||||
@ -433,7 +427,8 @@ diff_multivol (void)
|
||||
}
|
||||
|
||||
|
||||
fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
|
||||
struct fdbase f = fdbase (current_stat_info.file_name);
|
||||
int fd = f.fd == BADFD ? -1 : openat (f.fd, f.base, open_read_flags);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
@ -451,8 +446,7 @@ diff_multivol (void)
|
||||
else
|
||||
read_and_process (¤t_stat_info, process_rawdata);
|
||||
|
||||
status = close (fd);
|
||||
if (status != 0)
|
||||
if (close (fd) < 0)
|
||||
close_error (current_stat_info.file_name);
|
||||
}
|
||||
|
||||
@ -475,9 +469,9 @@ diff_archive (void)
|
||||
switch (current_header->header.typeflag)
|
||||
{
|
||||
default:
|
||||
ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
|
||||
quotearg_colon (current_stat_info.file_name),
|
||||
current_header->header.typeflag));
|
||||
paxerror (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:
|
||||
@ -496,11 +490,9 @@ diff_archive (void)
|
||||
diff_link ();
|
||||
break;
|
||||
|
||||
#ifdef HAVE_READLINK
|
||||
case SYMTYPE:
|
||||
diff_symlink ();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case CHRTYPE:
|
||||
case BLKTYPE:
|
||||
@ -526,22 +518,20 @@ diff_archive (void)
|
||||
void
|
||||
verify_volume (void)
|
||||
{
|
||||
int may_fail = 0;
|
||||
bool may_fail = false;
|
||||
if (removed_prefixes_p ())
|
||||
{
|
||||
WARN((0, 0,
|
||||
_("Archive contains file names with leading prefixes removed.")));
|
||||
may_fail = 1;
|
||||
paxwarn (0,
|
||||
_("Archive contains file names with leading prefixes removed."));
|
||||
may_fail = true;
|
||||
}
|
||||
if (transform_program_p ())
|
||||
{
|
||||
WARN((0, 0,
|
||||
_("Archive contains transformed file names.")));
|
||||
may_fail = 1;
|
||||
paxwarn (0, _("Archive contains transformed file names."));
|
||||
may_fail = true;
|
||||
}
|
||||
if (may_fail)
|
||||
WARN((0, 0,
|
||||
_("Verification may fail to locate original files.")));
|
||||
paxwarn (0, _("Verification may fail to locate original files."));
|
||||
|
||||
clear_directory_table ();
|
||||
|
||||
@ -565,7 +555,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]);
|
||||
@ -584,7 +574,7 @@ verify_volume (void)
|
||||
|
||||
if (status == HEADER_FAILURE)
|
||||
{
|
||||
int counter = 0;
|
||||
intmax_t counter = 0;
|
||||
|
||||
do
|
||||
{
|
||||
@ -595,10 +585,11 @@ verify_volume (void)
|
||||
}
|
||||
while (status == HEADER_FAILURE);
|
||||
|
||||
ERROR ((0, 0,
|
||||
ngettext ("VERIFY FAILURE: %d invalid header detected",
|
||||
"VERIFY FAILURE: %d invalid headers detected",
|
||||
counter), counter));
|
||||
paxerror (0,
|
||||
ngettext ("VERIFY FAILURE: %jd invalid header detected",
|
||||
"VERIFY FAILURE: %jd invalid headers detected",
|
||||
counter),
|
||||
counter);
|
||||
}
|
||||
if (status == HEADER_END_OF_FILE)
|
||||
break;
|
||||
@ -607,20 +598,18 @@ verify_volume (void)
|
||||
set_next_block_after (current_header);
|
||||
if (!ignore_zeros_option)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
if (status == HEADER_ZERO_BLOCK)
|
||||
break;
|
||||
WARNOPT (WARN_ALONE_ZERO_BLOCK,
|
||||
(0, 0, _("A lone zero block at %s"),
|
||||
STRINGIFY_BIGINT (current_block_ordinal (), buf)));
|
||||
warnopt (WARN_ALONE_ZERO_BLOCK,
|
||||
0, _("A lone zero block at %jd"),
|
||||
intmax (current_block_ordinal ()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
|
||||
decode_header (current_header, ¤t_stat_info, ¤t_format, true);
|
||||
diff_archive ();
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
}
|
||||
|
||||
656
src/create.c
656
src/create.c
File diff suppressed because it is too large
Load Diff
75
src/delete.c
75
src/delete.c
@ -1,6 +1,6 @@
|
||||
/* Delete entries from a tar archive.
|
||||
|
||||
Copyright 1988-2023 Free Software Foundation, Inc.
|
||||
Copyright 1988-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -23,18 +23,9 @@
|
||||
#include <rmt.h>
|
||||
|
||||
static union block *new_record;
|
||||
static int new_blocks;
|
||||
static idx_t 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;
|
||||
@ -61,12 +52,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 (INT_MULTIPLY_WRAPV (record_size, count, &increment)
|
||||
|| INT_ADD_WRAPV (position0, start_offset, &move_start)
|
||||
|| INT_ADD_WRAPV (move_start, increment, &position)
|
||||
if (ckd_mul (&increment, record_size, count)
|
||||
|| ckd_add (&move_start, position0, start_offset)
|
||||
|| ckd_add (&position, move_start, increment)
|
||||
|| position < 0)
|
||||
{
|
||||
ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0]));
|
||||
paxerror (EOVERFLOW, "lseek: %s", archive_name_array[0]);
|
||||
return;
|
||||
}
|
||||
else if (rmtlseek (archive, position, SEEK_SET) == position)
|
||||
@ -79,7 +70,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 (int move_back_flag)
|
||||
write_record (bool move_back_flag)
|
||||
{
|
||||
union block *save_record = record_start;
|
||||
record_start = new_record;
|
||||
@ -110,22 +101,21 @@ write_record (int move_back_flag)
|
||||
}
|
||||
|
||||
static void
|
||||
write_recent_blocks (union block *h, size_t blocks)
|
||||
write_recent_blocks (union block *h, idx_t blocks)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < blocks; i++)
|
||||
for (idx_t i = 0; i < blocks; i++)
|
||||
{
|
||||
new_record[new_blocks++] = h[i];
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
write_record (true);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_recent_bytes (char *data, size_t bytes)
|
||||
write_recent_bytes (char *data, idx_t bytes)
|
||||
{
|
||||
size_t blocks = bytes / BLOCKSIZE;
|
||||
size_t rest = bytes - blocks * BLOCKSIZE;
|
||||
idx_t blocks = bytes >> LG_BLOCKSIZE;
|
||||
idx_t rest = bytes & (BLOCKSIZE - 1);
|
||||
|
||||
write_recent_blocks ((union block *)data, blocks);
|
||||
memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
|
||||
@ -133,17 +123,15 @@ write_recent_bytes (char *data, size_t bytes)
|
||||
memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
|
||||
new_blocks++;
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
write_record (true);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_file (void)
|
||||
{
|
||||
off_t blocks_to_skip;
|
||||
|
||||
set_next_block_after (current_header);
|
||||
blocks_to_skip = (current_stat_info.stat.st_size
|
||||
+ BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
off_t size = current_stat_info.stat.st_size;
|
||||
off_t blocks_to_skip = (size >> LG_BLOCKSIZE) + !!(size & (BLOCKSIZE - 1));
|
||||
|
||||
while (record_end - current_block <= blocks_to_skip)
|
||||
{
|
||||
@ -151,7 +139,7 @@ flush_file (void)
|
||||
flush_archive ();
|
||||
if (record_end == current_block)
|
||||
/* Hit EOF */
|
||||
break;
|
||||
return;
|
||||
}
|
||||
current_block += blocks_to_skip;
|
||||
}
|
||||
@ -165,11 +153,11 @@ delete_archive_members (void)
|
||||
/* FIXME: Should clean the routine before cleaning these variables :-( */
|
||||
struct name *name;
|
||||
off_t blocks_to_keep = 0;
|
||||
int kept_blocks_in_record;
|
||||
ptrdiff_t kept_blocks_in_record;
|
||||
|
||||
name_gather ();
|
||||
open_archive (ACCESS_UPDATE);
|
||||
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
|
||||
acting_as_filter = streq (archive_name_array[0], "-");
|
||||
|
||||
/* Skip to the first member that matches the name list. */
|
||||
do
|
||||
@ -190,7 +178,7 @@ delete_archive_members (void)
|
||||
break;
|
||||
}
|
||||
name->found_count++;
|
||||
if (!ISFOUND (name))
|
||||
if (!isfound (name))
|
||||
{
|
||||
skim_member (acting_as_filter);
|
||||
break;
|
||||
@ -216,12 +204,12 @@ delete_archive_members (void)
|
||||
switch (previous_status)
|
||||
{
|
||||
case HEADER_STILL_UNREAD:
|
||||
WARN ((0, 0, _("This does not look like a tar archive")));
|
||||
paxwarn (0, _("This does not look like a tar archive"));
|
||||
FALLTHROUGH;
|
||||
case HEADER_SUCCESS:
|
||||
case HEADER_SUCCESS_EXTENDED:
|
||||
case HEADER_ZERO_BLOCK:
|
||||
ERROR ((0, 0, _("Skipping to next header")));
|
||||
paxerror (0, _("Skipping to next header"));
|
||||
FALLTHROUGH;
|
||||
case HEADER_FAILURE:
|
||||
break;
|
||||
@ -282,7 +270,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;
|
||||
@ -305,10 +293,11 @@ delete_archive_members (void)
|
||||
new_record[new_blocks] = *current_header;
|
||||
new_blocks++;
|
||||
blocks_to_keep
|
||||
= (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
= ((current_stat_info.stat.st_size >> LG_BLOCKSIZE)
|
||||
+ !!(current_stat_info.stat.st_size & (BLOCKSIZE - 1)));
|
||||
set_next_block_after (current_header);
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
write_record (true);
|
||||
|
||||
/* Copy data. */
|
||||
|
||||
@ -318,7 +307,7 @@ delete_archive_members (void)
|
||||
|
||||
while (blocks_to_keep)
|
||||
{
|
||||
int count;
|
||||
ptrdiff_t count;
|
||||
|
||||
if (current_block == record_end)
|
||||
{
|
||||
@ -343,7 +332,7 @@ delete_archive_members (void)
|
||||
kept_blocks_in_record -= count;
|
||||
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
write_record (true);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -359,7 +348,7 @@ delete_archive_members (void)
|
||||
break;
|
||||
|
||||
case HEADER_FAILURE:
|
||||
ERROR ((0, 0, _("Deleting non-header from archive")));
|
||||
paxerror (0, _("Deleting non-header from archive"));
|
||||
set_next_block_after (current_header);
|
||||
break;
|
||||
|
||||
@ -374,11 +363,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. */
|
||||
|
||||
int total_zero_blocks = 0;
|
||||
idx_t total_zero_blocks = 0;
|
||||
|
||||
do
|
||||
{
|
||||
int zero_blocks = blocking_factor - new_blocks;
|
||||
idx_t 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);
|
||||
@ -388,7 +377,7 @@ delete_archive_members (void)
|
||||
|
||||
if (! acting_as_filter && ! _isrmt (archive))
|
||||
{
|
||||
if (sys_truncate (archive))
|
||||
if (sys_truncate (archive) < 0)
|
||||
truncate_warn (archive_name_array[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* Per-directory exclusion files for tar.
|
||||
|
||||
Copyright 2014-2023 Free Software Foundation, Inc.
|
||||
Copyright 2014-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
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>
|
||||
@ -71,36 +72,31 @@ struct exclist
|
||||
void
|
||||
info_attach_exclist (struct tar_stat_info *dir)
|
||||
{
|
||||
struct excfile *file;
|
||||
struct exclist *head = NULL, *tail = NULL, *ent;
|
||||
struct vcs_ignore_file *vcsfile;
|
||||
|
||||
struct exclist *head = NULL, *tail = NULL;
|
||||
if (dir->exclude_list)
|
||||
return;
|
||||
for (file = excfile_head; file; file = file->next)
|
||||
|
||||
for (struct excfile *file = excfile_head; file; file = file->next)
|
||||
{
|
||||
if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
|
||||
if (faccessat (dir->fd, file->name, F_OK, 0) == 0)
|
||||
{
|
||||
FILE *fp;
|
||||
struct exclude *ex = NULL;
|
||||
int fd = subfile_open (dir, file->name, O_RDONLY);
|
||||
if (fd == -1)
|
||||
if (fd < 0)
|
||||
{
|
||||
open_error (file->name);
|
||||
continue;
|
||||
}
|
||||
fp = fdopen (fd, "r");
|
||||
FILE *fp = fdopen (fd, "r");
|
||||
if (!fp)
|
||||
{
|
||||
ERROR ((0, errno, _("%s: fdopen failed"), file->name));
|
||||
paxerror (errno, _("%s: fdopen failed"), file->name);
|
||||
close (fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ex)
|
||||
ex = new_exclude ();
|
||||
struct exclude *ex = new_exclude ();
|
||||
|
||||
vcsfile = get_vcs_ignore_file (file->name);
|
||||
struct vcs_ignore_file *vcsfile = get_vcs_ignore_file (file->name);
|
||||
|
||||
if (vcsfile->initfn)
|
||||
vcsfile->data = vcsfile->initfn (vcsfile->data);
|
||||
@ -109,16 +105,12 @@ info_attach_exclist (struct tar_stat_info *dir)
|
||||
FNM_FILE_NAME|EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED,
|
||||
'\n',
|
||||
vcsfile->data))
|
||||
{
|
||||
int e = errno;
|
||||
FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
|
||||
}
|
||||
paxfatal (errno, "%s", quotearg_colon (file->name));
|
||||
fclose (fp);
|
||||
|
||||
ent = xmalloc (sizeof (*ent));
|
||||
struct exclist *ent = xmalloc (sizeof *ent);
|
||||
ent->excluded = ex;
|
||||
ent->flags = file->flags == EXCL_DEFAULT
|
||||
? file->flags : vcsfile->flags;
|
||||
ent->flags = file->flags;
|
||||
ent->prev = tail;
|
||||
ent->next = NULL;
|
||||
|
||||
@ -178,12 +170,7 @@ excluded_name (char const *name, struct tar_stat_info *st)
|
||||
break;
|
||||
|
||||
if (!rname)
|
||||
{
|
||||
rname = name;
|
||||
/* Skip leading ./ */
|
||||
while (*rname == '.' && ISSLASH (rname[1]))
|
||||
rname += 2;
|
||||
}
|
||||
rname = name + dotslashlen (name);
|
||||
if ((result = excluded_file_name (ep->excluded, rname)))
|
||||
break;
|
||||
|
||||
@ -204,13 +191,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))
|
||||
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)
|
||||
!= WRDSE_OK)
|
||||
return;
|
||||
for (i = 0; i < ws.ws_wordc; i++)
|
||||
for (idx_t i = 0; i < ws.ws_wordc; i++)
|
||||
add_exclude (ex, ws.ws_wordv[i], options);
|
||||
wordsplit_free (&ws);
|
||||
}
|
||||
@ -219,7 +206,7 @@ static void
|
||||
git_addfn (struct exclude *ex, char const *pattern, int options,
|
||||
MAYBE_UNUSED void *data)
|
||||
{
|
||||
while (isspace (*pattern))
|
||||
while (c_isspace (*pattern))
|
||||
++pattern;
|
||||
if (*pattern == 0 || *pattern == '#')
|
||||
return;
|
||||
@ -232,7 +219,7 @@ static void
|
||||
bzr_addfn (struct exclude *ex, char const *pattern, int options,
|
||||
MAYBE_UNUSED void *data)
|
||||
{
|
||||
while (isspace (*pattern))
|
||||
while (c_isspace (*pattern))
|
||||
++pattern;
|
||||
if (*pattern == 0 || *pattern == '#')
|
||||
return;
|
||||
@ -267,26 +254,25 @@ static void
|
||||
hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
{
|
||||
int *hgopt = data;
|
||||
size_t len;
|
||||
|
||||
while (isspace (*pattern))
|
||||
while (c_isspace (*pattern))
|
||||
++pattern;
|
||||
if (*pattern == 0 || *pattern == '#')
|
||||
return;
|
||||
if (strncmp (pattern, "syntax:", 7) == 0)
|
||||
{
|
||||
for (pattern += 7; isspace (*pattern); ++pattern)
|
||||
for (pattern += 7; c_isspace (*pattern); ++pattern)
|
||||
;
|
||||
if (strcmp (pattern, "regexp") == 0)
|
||||
if (streq (pattern, "regexp"))
|
||||
/* FIXME: Regexps must be perl-style */
|
||||
*hgopt = EXCLUDE_REGEX;
|
||||
else if (strcmp (pattern, "glob") == 0)
|
||||
else if (streq (pattern, "glob"))
|
||||
*hgopt = EXCLUDE_WILDCARDS;
|
||||
/* Ignore unknown syntax */
|
||||
return;
|
||||
}
|
||||
|
||||
len = strlen(pattern);
|
||||
idx_t len = strlen (pattern);
|
||||
if (pattern[len-1] == '/')
|
||||
{
|
||||
char *p;
|
||||
@ -320,7 +306,7 @@ get_vcs_ignore_file (const char *name)
|
||||
struct vcs_ignore_file *p;
|
||||
|
||||
for (p = vcs_ignore_files; p->filename; p++)
|
||||
if (strcmp (p->filename, name) == 0)
|
||||
if (streq (p->filename, name))
|
||||
break;
|
||||
|
||||
return p;
|
||||
@ -332,5 +318,5 @@ exclude_vcs_ignores (void)
|
||||
struct vcs_ignore_file *p;
|
||||
|
||||
for (p = vcs_ignore_files; p->filename; p++)
|
||||
excfile_add (p->filename, EXCL_DEFAULT);
|
||||
excfile_add (p->filename, p->flags);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* Exit from GNU tar.
|
||||
|
||||
Copyright 2009-2023 Free Software Foundation, Inc.
|
||||
Copyright 2009-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
799
src/extract.c
799
src/extract.c
File diff suppressed because it is too large
Load Diff
653
src/incremen.c
653
src/incremen.c
File diff suppressed because it is too large
Load Diff
597
src/list.c
597
src/list.c
File diff suppressed because it is too large
Load Diff
119
src/map.c
119
src/map.c
@ -1,6 +1,6 @@
|
||||
/* Owner/group mapping for tar
|
||||
|
||||
Copyright 2015-2023 Free Software Foundation, Inc.
|
||||
Copyright 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -45,28 +45,26 @@ map_compare (void const *entry1, void const *entry2)
|
||||
return map1->orig_id == map2->orig_id;
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
parse_id (uintmax_t *retval,
|
||||
char const *arg, char const *what, uintmax_t maxval,
|
||||
char const *file, unsigned line)
|
||||
char const *file, intmax_t line)
|
||||
{
|
||||
uintmax_t v;
|
||||
char *p;
|
||||
|
||||
errno = 0;
|
||||
v = strtoumax (arg, &p, 10);
|
||||
if (*p || errno)
|
||||
bool overflow;
|
||||
*retval = stoint (arg, &p, &overflow, 0, maxval);
|
||||
|
||||
if ((p == arg) | *p)
|
||||
{
|
||||
error (0, 0, _("%s:%u: invalid %s: %s"), file, line, what, arg);
|
||||
return -1;
|
||||
error (0, 0, _("%s:%jd: invalid %s: %s"), file, line, what, arg);
|
||||
return false;
|
||||
}
|
||||
if (v > maxval)
|
||||
if (overflow)
|
||||
{
|
||||
error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
|
||||
return -1;
|
||||
error (0, 0, _("%s:%jd: %s out of range: %s"), file, line, what, arg);
|
||||
return false;
|
||||
}
|
||||
*retval = v;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -80,9 +78,9 @@ map_read (Hash_table **ptab, char const *file,
|
||||
ssize_t n;
|
||||
struct wordsplit ws;
|
||||
int wsopt;
|
||||
unsigned line;
|
||||
int err = 0;
|
||||
|
||||
intmax_t line;
|
||||
bool err = false;
|
||||
|
||||
fp = fopen (file, "r");
|
||||
if (!fp)
|
||||
open_fatal (file);
|
||||
@ -97,26 +95,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))
|
||||
FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
|
||||
file, line, wordsplit_strerror (&ws)));
|
||||
if (wordsplit (buf, &ws, wsopt) != WRDSE_OK)
|
||||
paxfatal (0, _("%s:%jd: 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:%u: malformed line"), file, line);
|
||||
err = 1;
|
||||
error (0, 0, _("%s:%jd: malformed line"), file, line);
|
||||
err = true;
|
||||
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 = 1;
|
||||
err = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -125,9 +123,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:%u: can't obtain %s of %s"),
|
||||
error (0, 0, _("%s:%jd: can't obtain %s of %s"),
|
||||
file, line, what, ws.ws_wordv[0]);
|
||||
err = 1;
|
||||
err = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -138,17 +136,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 = 1;
|
||||
err = true;
|
||||
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 = 1;
|
||||
err = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -158,9 +156,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:%u: can't obtain %s of %s"),
|
||||
error (0, 0, _("%s:%jd: can't obtain %s of %s"),
|
||||
file, line, what, ws.ws_wordv[1]);
|
||||
err = 1;
|
||||
err = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -169,9 +167,10 @@ 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, 0, map_hash, map_compare, 0)))
|
||||
|| (*ptab = hash_initialize (0, NULL, map_hash, map_compare,
|
||||
NULL)))
|
||||
&& hash_insert (*ptab, ent)))
|
||||
xalloc_die ();
|
||||
}
|
||||
@ -179,7 +178,7 @@ map_read (Hash_table **ptab, char const *file,
|
||||
wordsplit_free (&ws);
|
||||
fclose (fp);
|
||||
if (err)
|
||||
FATAL_ERROR ((0, 0, _("errors reading map file")));
|
||||
paxfatal (0, _("errors reading map file"));
|
||||
}
|
||||
|
||||
/* UID translation */
|
||||
@ -199,37 +198,28 @@ owner_map_read (char const *file)
|
||||
map_read (&owner_map, file, name_to_uid, "UID", TYPE_MAXIMUM (uid_t));
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
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 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (owner_option != (uid_t) -1)
|
||||
{
|
||||
*new_uid = owner_option;
|
||||
rc = 0;
|
||||
}
|
||||
uid_t minus_1 = -1;
|
||||
if (owner_option != minus_1)
|
||||
*new_uid = owner_option;
|
||||
if (owner_name_option)
|
||||
{
|
||||
*new_name = owner_name_option;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
*new_name = owner_name_option;
|
||||
}
|
||||
|
||||
/* GID translation */
|
||||
@ -249,35 +239,26 @@ group_map_read (char const *file)
|
||||
map_read (&group_map, file, name_to_gid, "GID", TYPE_MAXIMUM (gid_t));
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
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 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (group_option != (uid_t) -1)
|
||||
{
|
||||
*new_gid = group_option;
|
||||
rc = 0;
|
||||
}
|
||||
gid_t minus_1 = -1;
|
||||
if (group_option != minus_1)
|
||||
*new_gid = group_option;
|
||||
if (group_name_option)
|
||||
{
|
||||
*new_name = group_name_option;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
*new_name = group_name_option;
|
||||
}
|
||||
|
||||
754
src/misc.c
754
src/misc.c
File diff suppressed because it is too large
Load Diff
575
src/names.c
575
src/names.c
File diff suppressed because it is too large
Load Diff
517
src/sparse.c
517
src/sparse.c
@ -1,6 +1,6 @@
|
||||
/* Functions for dealing with sparse files
|
||||
|
||||
Copyright 2003-2023 Free Software Foundation, Inc.
|
||||
Copyright 2003-2026 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,6 +16,7 @@
|
||||
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"
|
||||
@ -40,8 +41,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 *, size_t);
|
||||
bool (*extract_region) (struct tar_sparse_file *, size_t);
|
||||
bool (*dump_region) (struct tar_sparse_file *, idx_t);
|
||||
bool (*extract_region) (struct tar_sparse_file *, idx_t);
|
||||
};
|
||||
|
||||
struct tar_sparse_file
|
||||
@ -73,12 +74,8 @@ dump_zeros (struct tar_sparse_file *file, off_t offset)
|
||||
|
||||
while (file->offset < offset)
|
||||
{
|
||||
size_t size = (BLOCKSIZE < offset - file->offset
|
||||
? BLOCKSIZE
|
||||
: offset - file->offset);
|
||||
ssize_t wrbytes;
|
||||
|
||||
wrbytes = write (file->fd, zero_buf, size);
|
||||
idx_t size = min (BLOCKSIZE, offset - file->offset);
|
||||
ssize_t wrbytes = write (file->fd, zero_buf, size);
|
||||
if (wrbytes <= 0)
|
||||
{
|
||||
if (wrbytes == 0)
|
||||
@ -131,7 +128,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, size_t i)
|
||||
tar_sparse_dump_region (struct tar_sparse_file *file, idx_t i)
|
||||
{
|
||||
if (file->optab->dump_region)
|
||||
return file->optab->dump_region (file, i);
|
||||
@ -139,7 +136,7 @@ tar_sparse_dump_region (struct tar_sparse_file *file, size_t i)
|
||||
}
|
||||
|
||||
static bool
|
||||
tar_sparse_extract_region (struct tar_sparse_file *file, size_t i)
|
||||
tar_sparse_extract_region (struct tar_sparse_file *file, idx_t i)
|
||||
{
|
||||
if (file->optab->extract_region)
|
||||
return file->optab->extract_region (file, i);
|
||||
@ -188,9 +185,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, size_t size)
|
||||
zero_block_p (char const *buffer, idx_t size)
|
||||
{
|
||||
while (size--)
|
||||
for (; size; size--)
|
||||
if (*buffer++)
|
||||
return false;
|
||||
return true;
|
||||
@ -200,10 +197,10 @@ static void
|
||||
sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp)
|
||||
{
|
||||
struct sp_array *sparse_map = st->sparse_map;
|
||||
size_t avail = st->sparse_map_avail;
|
||||
idx_t avail = st->sparse_map_avail;
|
||||
if (avail == st->sparse_map_size)
|
||||
st->sparse_map = sparse_map =
|
||||
x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map);
|
||||
xpalloc (sparse_map, &st->sparse_map_size, 1, -1, sizeof *sparse_map);
|
||||
sparse_map[avail] = *sp;
|
||||
st->sparse_map_avail = avail + 1;
|
||||
}
|
||||
@ -215,7 +212,6 @@ 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};
|
||||
|
||||
@ -224,9 +220,17 @@ sparse_scan_file_raw (struct tar_sparse_file *file)
|
||||
if (!tar_sparse_scan (file, scan_begin, NULL))
|
||||
return false;
|
||||
|
||||
while ((count = blocking_read (fd, buffer, sizeof buffer)) != 0
|
||||
&& count != SAFE_READ_ERROR)
|
||||
while (true)
|
||||
{
|
||||
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))
|
||||
{
|
||||
@ -249,6 +253,8 @@ 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
|
||||
@ -257,7 +263,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -300,7 +305,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 == (off_t)-1)
|
||||
if (data_offset < 0)
|
||||
/* ENXIO == EOF; error otherwise */
|
||||
{
|
||||
if (errno == ENXIO)
|
||||
@ -354,8 +359,7 @@ sparse_scan_file (struct tar_sparse_file *file)
|
||||
return true;
|
||||
#else
|
||||
if (hole_detection == HOLE_DETECTION_SEEK)
|
||||
WARN((0, 0,
|
||||
_("\"seek\" hole detection is not supported, using \"raw\".")));
|
||||
paxwarn (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
|
||||
@ -401,9 +405,8 @@ sparse_select_optab (struct tar_sparse_file *file)
|
||||
}
|
||||
|
||||
static bool
|
||||
sparse_dump_region (struct tar_sparse_file *file, size_t i)
|
||||
sparse_dump_region (struct tar_sparse_file *file, idx_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))
|
||||
@ -411,68 +414,50 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
|
||||
|
||||
while (bytes_left > 0)
|
||||
{
|
||||
size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
|
||||
size_t bytes_read;
|
||||
|
||||
blk = find_next_block ();
|
||||
bytes_read = full_read (file->fd, blk->buffer, bufsize);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
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;
|
||||
set_next_block_after (blk);
|
||||
|
||||
if (bytes_read < bufsize)
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
set_next_block_after (charptr (blk) + bufsize - 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
sparse_extract_region (struct tar_sparse_file *file, size_t i)
|
||||
sparse_extract_region (struct tar_sparse_file *file, idx_t i)
|
||||
{
|
||||
off_t write_size;
|
||||
|
||||
@ -489,17 +474,17 @@ sparse_extract_region (struct tar_sparse_file *file, size_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)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxerror (0, _("Unexpected EOF in archive"));
|
||||
return false;
|
||||
}
|
||||
set_next_block_after (blk);
|
||||
file->dumped_size += BLOCKSIZE;
|
||||
count = blocking_write (file->fd, blk->buffer, wrbytes);
|
||||
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);
|
||||
write_size -= count;
|
||||
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
|
||||
file->offset += count;
|
||||
@ -536,12 +521,10 @@ 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 (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
for (idx_t i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
rc = tar_sparse_dump_region (&file, i);
|
||||
}
|
||||
}
|
||||
@ -577,7 +560,6 @@ 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))
|
||||
{
|
||||
@ -591,7 +573,7 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
|
||||
file.offset = 0;
|
||||
|
||||
rc = tar_sparse_decode_header (&file);
|
||||
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
for (idx_t 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;
|
||||
@ -623,24 +605,14 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
|
||||
|
||||
while (beg < end)
|
||||
{
|
||||
size_t bytes_read;
|
||||
size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
|
||||
idx_t rdsize = min (end - beg, BLOCKSIZE);
|
||||
char diff_buffer[BLOCKSIZE];
|
||||
|
||||
bytes_read = full_read (file->fd, diff_buffer, rdsize);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
idx_t bytes_read = full_read (file->fd, diff_buffer, rdsize);
|
||||
if (bytes_read < 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);
|
||||
if (errno)
|
||||
read_diag_details (file->stat_info->orig_file_name, beg, rdsize);
|
||||
else
|
||||
report_difference (file->stat_info, _("Size differs"));
|
||||
return false;
|
||||
@ -662,7 +634,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, size_t i)
|
||||
check_data_region (struct tar_sparse_file *file, idx_t i)
|
||||
{
|
||||
off_t size_left;
|
||||
|
||||
@ -673,47 +645,38 @@ check_data_region (struct tar_sparse_file *file, size_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)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxerror (0, _("Unexpected EOF in archive"));
|
||||
return false;
|
||||
}
|
||||
set_next_block_after (blk);
|
||||
file->dumped_size += BLOCKSIZE;
|
||||
bytes_read = full_read (file->fd, diff_buffer, rdsize);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
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))
|
||||
{
|
||||
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);
|
||||
report_difference (file->stat_info, _("Contents differ"));
|
||||
return false;
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
|
||||
if (bytes_read < rdsize)
|
||||
{
|
||||
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);
|
||||
rdsize - bytes_read);
|
||||
else
|
||||
report_difference (¤t_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;
|
||||
}
|
||||
@ -723,7 +686,6 @@ 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))
|
||||
@ -735,7 +697,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
|
||||
|
||||
rc = tar_sparse_decode_header (&file);
|
||||
mv_begin_read (st);
|
||||
for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
for (idx_t i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
rc = check_sparse_region (&file,
|
||||
offset, file.stat_info->sparse_map[i].offset)
|
||||
@ -794,9 +756,10 @@ 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
|
||||
|| INT_ADD_OVERFLOW (sp.offset, sp.numbytes)
|
||||
|| file->stat_info->stat.st_size < sp.offset + sp.numbytes
|
||||
|| ckd_add (&size, sp.offset, sp.numbytes)
|
||||
|| file->stat_info->stat.st_size < size
|
||||
|| file->stat_info->archive_file_size < 0)
|
||||
return add_fail;
|
||||
|
||||
@ -819,45 +782,43 @@ 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 (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
|
||||
for (idx_t i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
|
||||
{
|
||||
rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
|
||||
if (rc != add_ok)
|
||||
break;
|
||||
}
|
||||
|
||||
for (ext_p = h->oldgnu_header.isextended;
|
||||
for (char ext_p = h->oldgnu_header.isextended;
|
||||
rc == add_ok && ext_p; ext_p = h->sparse_header.isextended)
|
||||
{
|
||||
h = find_next_block ();
|
||||
if (!h)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxerror (0, _("Unexpected EOF in archive"));
|
||||
return false;
|
||||
}
|
||||
set_next_block_after (h);
|
||||
for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
|
||||
for (idx_t 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)
|
||||
{
|
||||
ERROR ((0, 0, _("%s: invalid sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
paxerror (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, size_t *pindex,
|
||||
struct sparse *sp, size_t sparse_size)
|
||||
oldgnu_store_sparse_info (struct tar_sparse_file *file, idx_t *pindex,
|
||||
struct sparse *sp, idx_t sparse_size)
|
||||
{
|
||||
for (; *pindex < file->stat_info->sparse_map_avail
|
||||
&& sparse_size > 0; sparse_size--, sp++, ++*pindex)
|
||||
@ -874,7 +835,6 @@ 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;
|
||||
@ -886,7 +846,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);
|
||||
|
||||
i = 0;
|
||||
idx_t i = 0;
|
||||
oldgnu_store_sparse_info (file, &i,
|
||||
blk->oldgnu_header.sp,
|
||||
SPARSES_IN_OLDGNU_HEADER);
|
||||
@ -943,9 +903,8 @@ 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;
|
||||
int ext_p;
|
||||
char ext_p;
|
||||
enum oldgnu_add_status rc = add_ok;
|
||||
|
||||
file->stat_info->sparse_map_avail = 0;
|
||||
@ -954,7 +913,7 @@ star_get_sparse_info (struct tar_sparse_file *file)
|
||||
&& h->star_in_header.sp[0].offset[10] != '\0')
|
||||
{
|
||||
/* Old star format */
|
||||
for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
|
||||
for (idx_t i = 0; i < SPARSES_IN_STAR_HEADER; i++)
|
||||
{
|
||||
rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
|
||||
if (rc != add_ok)
|
||||
@ -970,19 +929,19 @@ star_get_sparse_info (struct tar_sparse_file *file)
|
||||
h = find_next_block ();
|
||||
if (!h)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxerror (0, _("Unexpected EOF in archive"));
|
||||
return false;
|
||||
}
|
||||
set_next_block_after (h);
|
||||
for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
|
||||
for (idx_t 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)
|
||||
{
|
||||
ERROR ((0, 0, _("%s: invalid sparse archive member"),
|
||||
file->stat_info->orig_file_name));
|
||||
paxerror (0, _("%s: invalid sparse archive member"),
|
||||
file->stat_info->orig_file_name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1102,7 +1061,6 @@ 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;
|
||||
@ -1114,7 +1072,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
|
||||
if (xheader_keyword_deleted_p ("GNU.sparse.map")
|
||||
|| tar_sparse_minor == 0)
|
||||
{
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
for (idx_t 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);
|
||||
@ -1128,7 +1086,7 @@ pax_dump_header_0 (struct tar_sparse_file *file)
|
||||
"%d/GNUSparseFile.%p/%f", 0);
|
||||
|
||||
xheader_string_begin (&file->stat_info->xhdr);
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
for (idx_t i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
{
|
||||
if (i)
|
||||
xheader_string_add (&file->stat_info->xhdr, ",");
|
||||
@ -1156,48 +1114,65 @@ 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 */
|
||||
p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
for (i = 0; i < file->stat_info->sparse_map_avail; i++)
|
||||
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 (map[i].offset, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
p = umaxtostr (map[i].numbytes, nbuf);
|
||||
size += strlen (p) + 1;
|
||||
size += floorlog10 (map[i].offset) + 2;
|
||||
size += floorlog10 (map[i].numbytes) + 2;
|
||||
}
|
||||
size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
file->stat_info->archive_file_size += size * BLOCKSIZE;
|
||||
file->dumped_size += size * BLOCKSIZE;
|
||||
size = (size + BLOCKSIZE - 1) & ~(BLOCKSIZE - 1);
|
||||
file->stat_info->archive_file_size += size;
|
||||
file->dumped_size += size;
|
||||
|
||||
/* Store sparse file identification */
|
||||
xheader_store ("GNU.sparse.major", file->stat_info, NULL);
|
||||
@ -1211,27 +1186,22 @@ 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;
|
||||
|
||||
blk = pax_start_header (file->stat_info);
|
||||
finish_header (file->stat_info, blk, block_ordinal);
|
||||
struct block_ptr bp;
|
||||
bp.block = pax_start_header (file->stat_info);
|
||||
finish_header (file->stat_info, bp.block, block_ordinal);
|
||||
free (file->stat_info->file_name);
|
||||
file->stat_info->file_name = save_file_name;
|
||||
|
||||
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.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++)
|
||||
{
|
||||
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");
|
||||
bp = dump_str_nl (bp, umaxtostr (map[i].offset, nbuf));
|
||||
bp = dump_str_nl (bp, umaxtostr (map[i].numbytes, nbuf));
|
||||
}
|
||||
memset (q, 0, BLOCKSIZE - (q - blk->buffer));
|
||||
set_next_block_after (blk);
|
||||
memset (bp.ptr, 0, BLOCKSIZE - (bp.ptr - bp.block->buffer));
|
||||
set_next_block_after (bp.block);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1245,23 +1215,52 @@ pax_dump_header (struct tar_sparse_file *file)
|
||||
pax_dump_header_0 (file) : pax_dump_header_1 (file);
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
|
||||
/* A success flag OK, a computed integer N, and block + ptr BP. */
|
||||
struct ok_n_block_ptr
|
||||
{
|
||||
uintmax_t u;
|
||||
char *arg_lim;
|
||||
bool ok;
|
||||
uintmax_t n;
|
||||
struct block_ptr bp;
|
||||
};
|
||||
|
||||
if (!ISDIGIT (*arg))
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
u = strtoumax (arg, &arg_lim, 10);
|
||||
|
||||
if (! (u <= maxval && errno != ERANGE) || *arg_lim)
|
||||
return false;
|
||||
|
||||
*num = u;
|
||||
return true;
|
||||
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 };
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -1269,82 +1268,38 @@ pax_decode_header (struct tar_sparse_file *file)
|
||||
{
|
||||
if (file->stat_info->sparse_major > 0)
|
||||
{
|
||||
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 ();
|
||||
off_t start = current_block_ordinal ();
|
||||
set_next_block_after (current_header);
|
||||
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));
|
||||
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);
|
||||
file->stat_info->sparse_map_avail = 0;
|
||||
for (i = 0; i < file->stat_info->sparse_map_size; i++)
|
||||
for (idx_t i = 0; i < file->stat_info->sparse_map_size; i++)
|
||||
{
|
||||
struct sp_array sp;
|
||||
|
||||
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;
|
||||
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;
|
||||
sparse_add_map (file->stat_info, &sp);
|
||||
}
|
||||
set_next_block_after (blk);
|
||||
set_next_block_after (bp.block);
|
||||
|
||||
file->dumped_size += BLOCKSIZE * (current_block_ordinal () - start);
|
||||
}
|
||||
|
||||
89
src/suffix.c
89
src/suffix.c
@ -1,5 +1,5 @@
|
||||
/* This file is part of GNU tar.
|
||||
Copyright 2007-2023 Free Software Foundation, Inc.
|
||||
Copyright 2007-2026 Free Software Foundation, Inc.
|
||||
|
||||
Written by Sergey Poznyakoff.
|
||||
|
||||
@ -19,18 +19,23 @@
|
||||
#include <system.h>
|
||||
#include "common.h"
|
||||
|
||||
#include <stdcountof.h>
|
||||
|
||||
struct compression_suffix
|
||||
{
|
||||
const char *suffix;
|
||||
size_t length;
|
||||
const char *program;
|
||||
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)))];
|
||||
};
|
||||
|
||||
static struct compression_suffix compression_suffixes[] = {
|
||||
static struct compression_suffix const compression_suffixes[] = {
|
||||
#define __CAT2__(a,b) a ## b
|
||||
#define S(s,p) #s, sizeof (#s) - 1, __CAT2__(p,_PROGRAM)
|
||||
{ "tar", 3, NULL },
|
||||
#define S(s, p) #s, __CAT2__(p,_PROGRAM)
|
||||
{ "tar", "" },
|
||||
{ S(gz, GZIP) },
|
||||
{ S(z, GZIP) },
|
||||
{ S(tgz, GZIP) },
|
||||
{ S(taz, GZIP) },
|
||||
{ S(Z, COMPRESS) },
|
||||
@ -43,63 +48,71 @@ static struct compression_suffix 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 (const char *name, size_t *ret_len)
|
||||
find_compression_suffix (char const *name, idx_t *ret_len)
|
||||
{
|
||||
char *suf = strrchr (name, '.');
|
||||
char const *suf = strrchr (name, '.');
|
||||
|
||||
if (suf)
|
||||
if (suf && suf[1] != 0 && suf[1] != '/')
|
||||
{
|
||||
size_t len;
|
||||
struct compression_suffix *p;
|
||||
|
||||
if (ret_len)
|
||||
*ret_len = suf - name;
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (struct compression_suffix const *p = compression_suffixes;
|
||||
p < compression_suffixes + countof (compression_suffixes);
|
||||
p++)
|
||||
if (streq (p->suffix, suf))
|
||||
return p;
|
||||
}
|
||||
else if (ret_len)
|
||||
*ret_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
find_compression_program (const char *name, const char *defprog)
|
||||
{
|
||||
struct compression_suffix const *p = find_compression_suffix (name, NULL);
|
||||
if (p)
|
||||
return p->program;
|
||||
return defprog;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
set_compression_program_by_suffix (const char *name, const char *defprog,
|
||||
bool verbose)
|
||||
{
|
||||
const char *program = find_compression_program (name, defprog);
|
||||
if (program)
|
||||
use_compress_program_option = program;
|
||||
idx_t len;
|
||||
struct compression_suffix const *p = find_compression_suffix (name, &len);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
strip_compression_suffix (const char *name)
|
||||
{
|
||||
char *s = NULL;
|
||||
size_t len;
|
||||
idx_t len;
|
||||
struct compression_suffix const *p = find_compression_suffix (name, &len);
|
||||
|
||||
if (p)
|
||||
|
||||
461
src/system.c
461
src/system.c
@ -1,6 +1,6 @@
|
||||
/* System-dependent calls for tar.
|
||||
|
||||
Copyright 2003-2023 Free Software Foundation, Inc.
|
||||
Copyright 2003-2026 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,13 +16,22 @@
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <system.h>
|
||||
#include <system-ioctl.h>
|
||||
|
||||
#if HAVE_SYS_MTIO_H
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/mtio.h>
|
||||
#endif
|
||||
|
||||
#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)
|
||||
@ -54,8 +63,8 @@ mtioseek (bool count_files, off_t count)
|
||||
? (count < 0 ? MTBSF : MTFSF)
|
||||
: (count < 0 ? MTBSR : MTFSR));
|
||||
if (! (count < 0
|
||||
? INT_SUBTRACT_WRAPV (0, count, &operation.mt_count)
|
||||
: INT_ADD_WRAPV (count, 0, &operation.mt_count))
|
||||
? ckd_sub (&operation.mt_count, 0, count)
|
||||
: ckd_add (&operation.mt_count, count, 0))
|
||||
&& (0 <= rmtioctl (archive, MTIOCTOP, &operation)
|
||||
|| (errno == EIO
|
||||
&& 0 <= rmtioctl (archive, MTIOCTOP, &operation))))
|
||||
@ -67,7 +76,7 @@ mtioseek (bool count_files, off_t count)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MSDOS
|
||||
#if !HAVE_WAITPID /* MingW, MSVC 14. */
|
||||
|
||||
bool
|
||||
sys_get_archive_stat (void)
|
||||
@ -86,7 +95,7 @@ sys_detect_dev_null_output (void)
|
||||
{
|
||||
static char const dev_null[] = "nul";
|
||||
|
||||
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|
||||
dev_null_output = (streq (archive_name_array[0], dev_null)
|
||||
|| (! _isrmt (archive)));
|
||||
}
|
||||
|
||||
@ -117,42 +126,43 @@ 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);
|
||||
}
|
||||
|
||||
size_t
|
||||
idx_t
|
||||
sys_write_archive_buffer (void)
|
||||
{
|
||||
return full_write (archive, record_start->buffer, record_size);
|
||||
return full_write (archive, charptr (record_start), record_size);
|
||||
}
|
||||
|
||||
/* Set ARCHIVE for writing, then compressing an archive. */
|
||||
void
|
||||
sys_child_open_for_compress (void)
|
||||
{
|
||||
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
|
||||
paxfatal (0, _("Cannot use compressed or remote archives"));
|
||||
}
|
||||
|
||||
/* Set ARCHIVE for uncompressing, then reading an archive. */
|
||||
void
|
||||
sys_child_open_for_uncompress (void)
|
||||
{
|
||||
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
|
||||
paxfatal (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)
|
||||
{
|
||||
@ -179,24 +189,23 @@ bool
|
||||
sys_file_is_archive (struct tar_stat_info *p)
|
||||
{
|
||||
return (!dev_null_output && !_isrmt (archive)
|
||||
&& p->stat.st_dev == archive_stat.st_dev
|
||||
&& p->stat.st_ino == archive_stat.st_ino);
|
||||
&& psame_inode (&p->stat, &archive_stat));
|
||||
}
|
||||
|
||||
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 = (strcmp (archive_name_array[0], dev_null) == 0
|
||||
dev_null_output = (streq (archive_name_array[0], dev_null)
|
||||
|| (! _isrmt (archive)
|
||||
&& S_ISCHR (archive_stat.st_mode)
|
||||
&& (dev_null_stat.st_ino != 0
|
||||
|| stat (dev_null, &dev_null_stat) == 0)
|
||||
&& archive_stat.st_ino == dev_null_stat.st_ino
|
||||
&& archive_stat.st_dev == dev_null_stat.st_dev));
|
||||
&& psame_inode (&archive_stat, &dev_null_stat)));
|
||||
}
|
||||
|
||||
void
|
||||
@ -206,7 +215,7 @@ sys_wait_for_child (pid_t child_pid, bool eof)
|
||||
{
|
||||
int wait_status;
|
||||
|
||||
while (waitpid (child_pid, &wait_status, 0) == -1)
|
||||
while (waitpid (child_pid, &wait_status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
waitpid_error (use_compress_program_option);
|
||||
@ -217,11 +226,11 @@ sys_wait_for_child (pid_t child_pid, bool eof)
|
||||
{
|
||||
int sig = WTERMSIG (wait_status);
|
||||
if (!(!eof && sig == SIGPIPE))
|
||||
FATAL_ERROR ((0, 0, _("Child died with signal %d"), sig));
|
||||
paxfatal (0, _("Child died with signal %d"), sig);
|
||||
}
|
||||
else if (WEXITSTATUS (wait_status) != 0)
|
||||
FATAL_ERROR ((0, 0, _("Child returned status %d"),
|
||||
WEXITSTATUS (wait_status)));
|
||||
paxfatal (0, _("Child returned status %d"),
|
||||
WEXITSTATUS (wait_status));
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +251,7 @@ sys_spawn_shell (void)
|
||||
else
|
||||
{
|
||||
int wait_status;
|
||||
while (waitpid (child, &wait_status, 0) == -1)
|
||||
while (waitpid (child, &wait_status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
waitpid_error (shell);
|
||||
@ -263,47 +272,48 @@ 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, (off_t) 0, SEEK_CUR);
|
||||
off_t pos = lseek (fd, 0, SEEK_CUR);
|
||||
return pos < 0 ? -1 : ftruncate (fd, pos);
|
||||
}
|
||||
|
||||
/* Return nonzero if NAME is the name of a regular file, or if the file
|
||||
/* Return true 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 int
|
||||
static bool
|
||||
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;
|
||||
}
|
||||
|
||||
size_t
|
||||
idx_t
|
||||
sys_write_archive_buffer (void)
|
||||
{
|
||||
return rmtwrite (archive, record_start->buffer, record_size);
|
||||
return rmtwrite (archive, charptr (record_start), record_size);
|
||||
}
|
||||
|
||||
#define PREAD 0 /* read file descriptor from pipe() */
|
||||
#define PWRITE 1 /* write file descriptor from pipe() */
|
||||
/* Read and write file descriptors from a pipe(pipefd) call. */
|
||||
enum { PREAD, PWRITE };
|
||||
|
||||
/* 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
|
||||
@ -312,10 +322,7 @@ xdup2 (int from, int into)
|
||||
if (from != into)
|
||||
{
|
||||
if (dup2 (from, into) < 0)
|
||||
{
|
||||
int e = errno;
|
||||
FATAL_ERROR ((0, e, _("Cannot dup2")));
|
||||
}
|
||||
paxfatal (errno, _("Cannot dup2"));
|
||||
xclose (from);
|
||||
}
|
||||
}
|
||||
@ -327,7 +334,7 @@ wait_for_grandchild (pid_t pid)
|
||||
int wait_status;
|
||||
int exit_code = 0;
|
||||
|
||||
while (waitpid (pid, &wait_status, 0) == -1)
|
||||
while (waitpid (pid, &wait_status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
waitpid_error (use_compress_program_option);
|
||||
@ -384,7 +391,7 @@ sys_child_open_for_compress (void)
|
||||
|
||||
/* We don't need a grandchild tar. Open the archive and launch the
|
||||
compressor. */
|
||||
if (strcmp (archive_name_array[0], "-"))
|
||||
if (!streq (archive_name_array[0], "-"))
|
||||
{
|
||||
archive = creat (archive_name_array[0], MODE_RW);
|
||||
if (archive < 0)
|
||||
@ -426,7 +433,7 @@ sys_child_open_for_compress (void)
|
||||
xdup2 (child_pipe[PREAD], STDIN_FILENO);
|
||||
xclose (child_pipe[PWRITE]);
|
||||
|
||||
if (strcmp (archive_name_array[0], "-") == 0)
|
||||
if (streq (archive_name_array[0], "-"))
|
||||
archive = STDOUT_FILENO;
|
||||
else
|
||||
{
|
||||
@ -439,20 +446,20 @@ sys_child_open_for_compress (void)
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t status = 0;
|
||||
ptrdiff_t status = 0;
|
||||
char *cursor;
|
||||
size_t length;
|
||||
idx_t length;
|
||||
|
||||
/* Assemble a record. */
|
||||
|
||||
for (length = 0, cursor = record_start->buffer;
|
||||
for (length = 0, cursor = charptr (record_start);
|
||||
length < record_size;
|
||||
length += status, cursor += status)
|
||||
{
|
||||
size_t size = record_size - length;
|
||||
idx_t size = record_size - length;
|
||||
|
||||
status = safe_read (STDIN_FILENO, cursor, size);
|
||||
if (status == SAFE_READ_ERROR)
|
||||
if (status < 0)
|
||||
read_fatal (use_compress_program_option);
|
||||
if (status == 0)
|
||||
break;
|
||||
@ -468,7 +475,7 @@ sys_child_open_for_compress (void)
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
memset (record_start->buffer + length, 0, record_size - length);
|
||||
memset (charptr (record_start) + length, 0, record_size - length);
|
||||
status = sys_write_archive_buffer ();
|
||||
if (status != record_size)
|
||||
archive_write_error (status);
|
||||
@ -501,24 +508,22 @@ run_decompress_program (void)
|
||||
{
|
||||
if (prog)
|
||||
{
|
||||
WARNOPT (WARN_DECOMPRESS_PROGRAM,
|
||||
(0, errno, _("cannot run %s"), prog));
|
||||
WARNOPT (WARN_DECOMPRESS_PROGRAM,
|
||||
(0, 0, _("trying %s"), p));
|
||||
warnopt (WARN_DECOMPRESS_PROGRAM, errno, _("cannot run %s"), prog);
|
||||
warnopt (WARN_DECOMPRESS_PROGRAM, 0, _("trying %s"), p);
|
||||
}
|
||||
if (wordsplit (p, &ws, wsflags))
|
||||
FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
|
||||
p, wordsplit_strerror (&ws)));
|
||||
if (wordsplit (p, &ws, wsflags) != WRDSE_OK)
|
||||
paxfatal (0, _("cannot split string '%s': %s"),
|
||||
p, wordsplit_strerror (&ws));
|
||||
wsflags |= WRDSF_REUSE;
|
||||
memmove(ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
|
||||
sizeof(ws.ws_wordv[0])*ws.ws_wordc);
|
||||
memmove (ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
|
||||
ws.ws_wordc * sizeof *ws.ws_wordv);
|
||||
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)
|
||||
FATAL_ERROR ((0, 0, _("unable to run decompression program")));
|
||||
paxfatal (0, _("unable to run decompression program"));
|
||||
exec_fatal (prog);
|
||||
}
|
||||
|
||||
@ -556,7 +561,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 (strcmp (archive_name_array[0], "-") != 0
|
||||
if (!streq (archive_name_array[0], "-")
|
||||
&& !_remdev (archive_name_array[0])
|
||||
&& is_regular_file (archive_name_array[0]))
|
||||
{
|
||||
@ -596,7 +601,7 @@ sys_child_open_for_uncompress (void)
|
||||
xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
|
||||
xclose (child_pipe[PREAD]);
|
||||
|
||||
if (strcmp (archive_name_array[0], "-") == 0)
|
||||
if (streq (archive_name_array[0], "-"))
|
||||
archive = STDIN_FILENO;
|
||||
else
|
||||
archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
|
||||
@ -606,34 +611,26 @@ sys_child_open_for_uncompress (void)
|
||||
|
||||
/* Let's read the archive and pipe it into stdout. */
|
||||
|
||||
while (1)
|
||||
while (true)
|
||||
{
|
||||
char *cursor;
|
||||
size_t maximum;
|
||||
size_t count;
|
||||
size_t status;
|
||||
|
||||
clear_read_error_count ();
|
||||
|
||||
error_loop:
|
||||
status = rmtread (archive, record_start->buffer, record_size);
|
||||
if (status == SAFE_READ_ERROR)
|
||||
{
|
||||
archive_read_error ();
|
||||
goto error_loop;
|
||||
}
|
||||
if (status == 0)
|
||||
ptrdiff_t n;
|
||||
while ((n = rmtread (archive, charptr (record_start), record_size)) < 0)
|
||||
archive_read_error ();
|
||||
if (n == 0)
|
||||
break;
|
||||
cursor = record_start->buffer;
|
||||
maximum = status;
|
||||
while (maximum)
|
||||
|
||||
char *cursor = charptr (record_start);
|
||||
do
|
||||
{
|
||||
count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE;
|
||||
idx_t count = min (n, BLOCKSIZE);
|
||||
if (full_write (STDOUT_FILENO, cursor, count) != count)
|
||||
write_error (use_compress_program_option);
|
||||
cursor += count;
|
||||
maximum -= count;
|
||||
n -= count;
|
||||
}
|
||||
while (n);
|
||||
}
|
||||
|
||||
xclose (STDOUT_FILENO);
|
||||
@ -646,11 +643,8 @@ sys_child_open_for_uncompress (void)
|
||||
static void
|
||||
dec_to_env (char const *envar, uintmax_t num)
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
char *numstr;
|
||||
|
||||
numstr = STRINGIFY_BIGINT (num, buf);
|
||||
if (setenv (envar, numstr, 1) != 0)
|
||||
char numstr[UINTMAX_STRSIZE_BOUND];
|
||||
if (setenv (envar, umaxtostr (num, numstr), 1) < 0)
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
@ -658,17 +652,19 @@ 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, unsigned long num)
|
||||
oct_to_env (char const *envar, mode_t m)
|
||||
{
|
||||
char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
|
||||
|
||||
snprintf (buf, sizeof buf, "0%lo", num);
|
||||
if (setenv (envar, buf, 1) != 0)
|
||||
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)
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
@ -677,7 +673,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
|
||||
@ -690,7 +686,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 ();
|
||||
}
|
||||
|
||||
@ -745,7 +741,7 @@ static pid_t global_pid;
|
||||
static void (*pipe_handler) (int sig);
|
||||
|
||||
int
|
||||
sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
|
||||
sys_exec_command (char *file_name, char typechar, struct tar_stat_info *st)
|
||||
{
|
||||
int p[2];
|
||||
|
||||
@ -778,7 +774,7 @@ sys_wait_command (void)
|
||||
return;
|
||||
|
||||
signal (SIGPIPE, pipe_handler);
|
||||
while (waitpid (global_pid, &status, 0) == -1)
|
||||
while (waitpid (global_pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
global_pid = -1;
|
||||
@ -789,53 +785,65 @@ sys_wait_command (void)
|
||||
if (WIFEXITED (status))
|
||||
{
|
||||
if (!ignore_command_error_option && WEXITSTATUS (status))
|
||||
ERROR ((0, 0, _("%lu: Child returned status %d"),
|
||||
(unsigned long) global_pid, WEXITSTATUS (status)));
|
||||
paxerror (0, _("%jd: Child returned status %d"),
|
||||
intmax (global_pid), WEXITSTATUS (status));
|
||||
}
|
||||
else if (WIFSIGNALED (status))
|
||||
{
|
||||
WARN ((0, 0, _("%lu: Child terminated on signal %d"),
|
||||
(unsigned long) global_pid, WTERMSIG (status)));
|
||||
paxwarn (0, _("%jd: Child terminated on signal %d"),
|
||||
intmax (global_pid), WTERMSIG (status));
|
||||
}
|
||||
else
|
||||
ERROR ((0, 0, _("%lu: Child terminated on unknown reason"),
|
||||
(unsigned long) global_pid));
|
||||
paxerror (0, _("%jd: Child terminated on unknown reason"),
|
||||
intmax (global_pid));
|
||||
|
||||
global_pid = -1;
|
||||
}
|
||||
|
||||
int
|
||||
sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
sys_exec_info_script (const char **archive_name, intmax_t 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 = xfork ();
|
||||
pid_t pid = xfork ();
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
/* Master */
|
||||
|
||||
int rc;
|
||||
int status;
|
||||
char *buf = NULL;
|
||||
size_t size = 0;
|
||||
FILE *fp;
|
||||
|
||||
xclose (p[PWRITE]);
|
||||
fp = fdopen (p[PREAD], "r");
|
||||
rc = getline (&buf, &size, fp);
|
||||
fclose (fp);
|
||||
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;
|
||||
}
|
||||
|
||||
if (rc > 0 && buf[rc-1] == '\n')
|
||||
buf[--rc] = 0;
|
||||
|
||||
while (waitpid (pid, &status, 0) == -1)
|
||||
while (waitpid (pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
signal (SIGPIPE, saved_handler);
|
||||
@ -844,31 +852,19 @@ sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
}
|
||||
|
||||
signal (SIGPIPE, saved_handler);
|
||||
|
||||
if (WIFEXITED (status))
|
||||
{
|
||||
if (WEXITSTATUS (status) == 0 && rc > 0)
|
||||
*archive_name = buf;
|
||||
else
|
||||
free (buf);
|
||||
return WEXITSTATUS (status);
|
||||
}
|
||||
|
||||
free (buf);
|
||||
return -1;
|
||||
return WIFEXITED (status) ? WEXITSTATUS (status) : -1;
|
||||
}
|
||||
|
||||
/* Child */
|
||||
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);
|
||||
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_SUBCOMMAND", subcommand_string (subcommand_option), 1);
|
||||
setenv ("TAR_FORMAT",
|
||||
archive_format_string (current_format == DEFAULT_FORMAT ?
|
||||
archive_format : current_format), 1);
|
||||
setenv ("TAR_FD", STRINGIFY_BIGINT (p[PWRITE], uintbuf), 1);
|
||||
dec_to_env ("TAR_FD", p[PWRITE]);
|
||||
|
||||
xclose (p[PREAD]);
|
||||
|
||||
@ -879,12 +875,9 @@ sys_exec_info_script (const char **archive_name, int volume_number)
|
||||
void
|
||||
sys_exec_checkpoint_script (const char *script_name,
|
||||
const char *archive_name,
|
||||
int checkpoint_number)
|
||||
intmax_t checkpoint_number)
|
||||
{
|
||||
pid_t pid;
|
||||
char uintbuf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
pid = xfork ();
|
||||
pid_t pid = xfork ();
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
@ -892,7 +885,7 @@ sys_exec_checkpoint_script (const char *script_name,
|
||||
|
||||
int status;
|
||||
|
||||
while (waitpid (pid, &status, 0) == -1)
|
||||
while (waitpid (pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
waitpid_error (script_name);
|
||||
@ -903,17 +896,175 @@ sys_exec_checkpoint_script (const char *script_name,
|
||||
}
|
||||
|
||||
/* Child */
|
||||
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);
|
||||
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));
|
||||
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 */
|
||||
|
||||
133
src/tar.h
133
src/tar.h
@ -1,6 +1,6 @@
|
||||
/* GNU tar Archive Format description.
|
||||
|
||||
Copyright 1988-2023 Free Software Foundation, Inc.
|
||||
Copyright 1988-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -48,34 +48,41 @@ struct posix_header
|
||||
#define TVERSLEN 2
|
||||
|
||||
/* Values used in typeflag field. */
|
||||
#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 */
|
||||
enum
|
||||
{
|
||||
|
||||
#define XHDTYPE 'x' /* Extended header referring to the
|
||||
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
|
||||
next file in the archive */
|
||||
#define XGLTYPE 'g' /* Global extended header */
|
||||
XGLTYPE = 'g' /* Global extended header */
|
||||
};
|
||||
|
||||
/* Bits used in the mode field, values in octal. */
|
||||
#define TSUID 04000 /* set UID on execution */
|
||||
#define TSGID 02000 /* set GID on execution */
|
||||
#define TSVTX 01000 /* reserved */
|
||||
enum
|
||||
{
|
||||
TSUID = 04000, /* set UID on execution */
|
||||
TSGID = 02000, /* set GID on execution */
|
||||
TSVTX = 01000, /* reserved */
|
||||
/* file permissions */
|
||||
#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 */
|
||||
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 */
|
||||
};
|
||||
|
||||
/* tar Header Block, GNU extensions. */
|
||||
|
||||
@ -111,9 +118,12 @@ struct sparse
|
||||
necessary. The following constants tell how many sparse descriptors fit
|
||||
in each kind of header able to hold them. */
|
||||
|
||||
#define SPARSES_IN_EXTRA_HEADER 16
|
||||
#define SPARSES_IN_OLDGNU_HEADER 4
|
||||
#define SPARSES_IN_SPARSE_HEADER 21
|
||||
enum
|
||||
{
|
||||
SPARSES_IN_EXTRA_HEADER = 16,
|
||||
SPARSES_IN_OLDGNU_HEADER = 4,
|
||||
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
|
||||
@ -168,29 +178,32 @@ 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) */
|
||||
|
||||
/* 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'
|
||||
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',
|
||||
|
||||
/* 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 linkname. */
|
||||
GNUTYPE_LONGLINK = 'K',
|
||||
|
||||
/* Identifies the *next* file on the tape as having a long name. */
|
||||
#define GNUTYPE_LONGNAME 'L'
|
||||
/* Identifies the *next* file on the tape as having a long name. */
|
||||
GNUTYPE_LONGNAME = 'L',
|
||||
|
||||
/* This is the continuation of a file that began on another volume. */
|
||||
#define GNUTYPE_MULTIVOL 'M'
|
||||
/* This is the continuation of a file that began on another volume. */
|
||||
GNUTYPE_MULTIVOL = 'M',
|
||||
|
||||
/* This is for sparse files. */
|
||||
#define GNUTYPE_SPARSE 'S'
|
||||
/* This is for sparse files. */
|
||||
GNUTYPE_SPARSE = 'S',
|
||||
|
||||
/* This file is a tape/volume header. Ignore it on extraction. */
|
||||
#define GNUTYPE_VOLHDR 'V'
|
||||
/* This file is a tape/volume header. Ignore it on extraction. */
|
||||
GNUTYPE_VOLHDR = 'V',
|
||||
|
||||
/* Solaris extended header */
|
||||
#define SOLARIS_XHDTYPE 'X'
|
||||
/* Solaris extended header. */
|
||||
SOLARIS_XHDTYPE = 'X'
|
||||
};
|
||||
|
||||
/* J@"org Schilling star header */
|
||||
/* Jörg Schilling star header. */
|
||||
|
||||
struct star_header
|
||||
{ /* byte offset */
|
||||
@ -215,8 +228,11 @@ struct star_header
|
||||
/* 500 */
|
||||
};
|
||||
|
||||
#define SPARSES_IN_STAR_HEADER 4
|
||||
#define SPARSES_IN_STAR_EXT_HEADER 21
|
||||
enum
|
||||
{
|
||||
SPARSES_IN_STAR_HEADER = 4,
|
||||
SPARSES_IN_STAR_EXT_HEADER = 21
|
||||
};
|
||||
|
||||
struct star_in_header
|
||||
{
|
||||
@ -245,8 +261,9 @@ struct star_ext_header
|
||||
|
||||
/* tar Header Block, overall structure. */
|
||||
|
||||
/* tar files are made in basic blocks of this size. */
|
||||
#define BLOCKSIZE 512
|
||||
/* 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 };
|
||||
|
||||
enum archive_format
|
||||
{
|
||||
@ -271,9 +288,9 @@ struct sp_array
|
||||
struct xheader
|
||||
{
|
||||
struct obstack *stk;
|
||||
size_t size;
|
||||
idx_t size;
|
||||
char *buffer;
|
||||
uintmax_t string_length;
|
||||
idx_t string_length;
|
||||
};
|
||||
|
||||
/* Information about xattrs for a file. */
|
||||
@ -281,14 +298,14 @@ struct xattr_array
|
||||
{
|
||||
char *xkey;
|
||||
char *xval_ptr;
|
||||
size_t xval_len;
|
||||
idx_t xval_len;
|
||||
};
|
||||
|
||||
struct xattr_map
|
||||
{
|
||||
struct xattr_array *xm_map;
|
||||
size_t xm_size; /* Size of the xattr map */
|
||||
size_t xm_max; /* Max. number of entries in xattr_map */
|
||||
idx_t xm_size; /* Size of the xattr map */
|
||||
idx_t xm_max; /* Max. number of entries in xattr_map */
|
||||
};
|
||||
|
||||
struct tar_stat_info
|
||||
@ -306,10 +323,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. */
|
||||
size_t acls_a_len; /* Access ACLs for the current archive entry. */
|
||||
idx_t acls_a_len; /* Access ACLs for the current archive entry. */
|
||||
|
||||
char *acls_d_ptr; /* Default ACLs for the current archive entry. */
|
||||
size_t acls_d_len; /* Default ACLs for the current archive entry. */
|
||||
idx_t acls_d_len; /* Default ACLs for the current archive entry. */
|
||||
|
||||
struct stat stat; /* regular filesystem stat */
|
||||
|
||||
@ -325,12 +342,12 @@ struct tar_stat_info
|
||||
bool is_sparse; /* Is the file sparse */
|
||||
|
||||
/* For sparse files: */
|
||||
unsigned sparse_major;
|
||||
unsigned sparse_minor;
|
||||
size_t sparse_map_avail; /* Index to the first unused element in
|
||||
intmax_t sparse_major;
|
||||
intmax_t sparse_minor;
|
||||
idx_t sparse_map_avail; /* Index to the first unused element in
|
||||
sparse_map array. Zero if the file is
|
||||
not sparse */
|
||||
size_t sparse_map_size; /* Size of the sparse map */
|
||||
idx_t sparse_map_size; /* Size of the sparse map */
|
||||
struct sp_array *sparse_map;
|
||||
|
||||
off_t real_size; /* The real size of sparse file */
|
||||
|
||||
260
src/transform.c
260
src/transform.c
@ -1,5 +1,5 @@
|
||||
/* This file is part of GNU tar.
|
||||
Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
Copyright 2006-2026 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,6 +16,8 @@
|
||||
|
||||
#include <system.h>
|
||||
#include <regex.h>
|
||||
#include <mcel.h>
|
||||
#include <quotearg.h>
|
||||
#include "common.h"
|
||||
|
||||
enum transform_type
|
||||
@ -49,9 +51,9 @@ struct replace_segm
|
||||
struct
|
||||
{
|
||||
char *ptr;
|
||||
size_t size;
|
||||
idx_t size;
|
||||
} literal; /* type == segm_literal */
|
||||
size_t ref; /* type == segm_backref */
|
||||
idx_t ref; /* type == segm_backref */
|
||||
enum case_ctl_type ctl; /* type == segm_case_ctl */
|
||||
} v;
|
||||
};
|
||||
@ -61,11 +63,11 @@ struct transform
|
||||
struct transform *next;
|
||||
enum transform_type transform_type;
|
||||
int flags;
|
||||
unsigned match_number;
|
||||
idx_t match_number;
|
||||
regex_t regex;
|
||||
/* Compiled replacement expression */
|
||||
struct replace_segm *repl_head, *repl_tail;
|
||||
size_t segm_count; /* Number of elements in the above list */
|
||||
idx_t segm_count; /* Number of elements in the above list */
|
||||
};
|
||||
|
||||
|
||||
@ -102,7 +104,7 @@ add_segment (struct transform *tf)
|
||||
static void
|
||||
add_literal_segment (struct transform *tf, const char *str, const char *end)
|
||||
{
|
||||
size_t len = end - str;
|
||||
idx_t len = end - str;
|
||||
if (len)
|
||||
{
|
||||
struct replace_segm *segm = add_segment (tf);
|
||||
@ -115,7 +117,7 @@ add_literal_segment (struct transform *tf, const char *str, const char *end)
|
||||
}
|
||||
|
||||
static void
|
||||
add_char_segment (struct transform *tf, int chr)
|
||||
add_char_segment (struct transform *tf, char chr)
|
||||
{
|
||||
struct replace_segm *segm = add_segment (tf);
|
||||
segm->type = segm_literal;
|
||||
@ -126,15 +128,15 @@ add_char_segment (struct transform *tf, int chr)
|
||||
}
|
||||
|
||||
static void
|
||||
add_backref_segment (struct transform *tf, size_t ref)
|
||||
add_backref_segment (struct transform *tf, idx_t ref)
|
||||
{
|
||||
struct replace_segm *segm = add_segment (tf);
|
||||
segm->type = segm_backref;
|
||||
segm->v.ref = ref;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_xform_flags (int *pflags, int c)
|
||||
static bool
|
||||
parse_xform_flags (int *pflags, char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
@ -163,9 +165,9 @@ parse_xform_flags (int *pflags, int c)
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -179,8 +181,7 @@ add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl)
|
||||
static const char *
|
||||
parse_transform_expr (const char *expr)
|
||||
{
|
||||
int delim;
|
||||
int i, j, rc;
|
||||
idx_t i, j;
|
||||
char *str, *beg, *cur;
|
||||
const char *p;
|
||||
int cflags = 0;
|
||||
@ -198,18 +199,17 @@ parse_transform_expr (const char *expr)
|
||||
expr++;
|
||||
break;
|
||||
}
|
||||
if (parse_xform_flags (&transform_flags, *expr))
|
||||
USAGE_ERROR ((0, 0, _("Unknown transform flag: %c"),
|
||||
*expr));
|
||||
if (!parse_xform_flags (&transform_flags, *expr))
|
||||
paxusage (_("Unknown transform flag: %c"), *expr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
|
||||
paxusage (_("Invalid transform expression"));
|
||||
}
|
||||
|
||||
delim = expr[1];
|
||||
char delim = expr[1];
|
||||
if (!delim)
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
|
||||
paxusage (_("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)
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
|
||||
paxusage (_("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)
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression")));
|
||||
paxusage (_("Invalid transform expression"));
|
||||
|
||||
/* Check flags */
|
||||
tf->transform_type = transform_first;
|
||||
@ -247,14 +247,16 @@ 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':
|
||||
tf->match_number = strtoul (p, (char**) &p, 0);
|
||||
p--;
|
||||
{
|
||||
char *endp;
|
||||
tf->match_number = stoint (p, &endp, NULL, 0, IDX_MAX);
|
||||
p = endp - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (parse_xform_flags (&tf->flags, *p))
|
||||
USAGE_ERROR ((0, 0, _("Unknown flag in transform expression: %c"),
|
||||
*p));
|
||||
if (!parse_xform_flags (&tf->flags, *p))
|
||||
paxusage (_("Unknown flag in transform expression: %c"), *p);
|
||||
}
|
||||
|
||||
if (*p == ';')
|
||||
@ -265,13 +267,13 @@ parse_transform_expr (const char *expr)
|
||||
memcpy (str, expr + 2, i - 2);
|
||||
str[i - 2] = 0;
|
||||
|
||||
rc = regcomp (&tf->regex, str, cflags);
|
||||
int rc = regcomp (&tf->regex, str, cflags);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
char errbuf[512];
|
||||
regerror (rc, &tf->regex, errbuf, sizeof (errbuf));
|
||||
USAGE_ERROR ((0, 0, _("Invalid transform expression: %s"), errbuf));
|
||||
paxusage (_("Invalid transform expression: %s"), errbuf);
|
||||
}
|
||||
|
||||
if (str[0] == '^' || (i > 2 && str[i - 3] == '$'))
|
||||
@ -289,17 +291,18 @@ 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':
|
||||
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);
|
||||
{
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
@ -416,74 +419,56 @@ 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;
|
||||
size_t nmatches = 0;
|
||||
idx_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 */
|
||||
|
||||
/* 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));
|
||||
regmatch_t *rmp = xinmalloc (tf->regex.re_nsub + 1, sizeof *rmp);
|
||||
|
||||
while (*input)
|
||||
{
|
||||
size_t disp;
|
||||
char *ptr;
|
||||
idx_t disp;
|
||||
|
||||
rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0);
|
||||
|
||||
@ -509,32 +494,28 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
switch (segm->type)
|
||||
{
|
||||
case segm_literal: /* Literal segment */
|
||||
if (case_ctl == ctl_stop)
|
||||
ptr = segm->v.literal.ptr;
|
||||
else
|
||||
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)
|
||||
{
|
||||
ptr = run_case_conv (case_ctl,
|
||||
segm->v.literal.ptr,
|
||||
segm->v.literal.size);
|
||||
CASE_CTL_RESET();
|
||||
case_ctl = save_ctl;
|
||||
save_ctl = ctl_stop;
|
||||
}
|
||||
obstack_grow (&stk, ptr, segm->v.literal.size);
|
||||
break;
|
||||
|
||||
case segm_backref: /* Back-reference segment */
|
||||
if (rmp[segm->v.ref].rm_so != -1
|
||||
&& rmp[segm->v.ref].rm_eo != -1)
|
||||
if (0 <= rmp[segm->v.ref].rm_so
|
||||
&& 0 <= rmp[segm->v.ref].rm_eo)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -581,11 +562,11 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
free (rmp);
|
||||
}
|
||||
|
||||
static bool
|
||||
static void
|
||||
_transform_name_to_obstack (int flags, char *input, char **output)
|
||||
{
|
||||
struct transform *tf;
|
||||
bool alloced = false;
|
||||
bool ok = false;
|
||||
|
||||
if (!stk_init)
|
||||
{
|
||||
@ -599,38 +580,53 @@ _transform_name_to_obstack (int flags, char *input, char **output)
|
||||
{
|
||||
_single_transform_name_to_obstack (tf, input);
|
||||
input = obstack_finish (&stk);
|
||||
alloced = true;
|
||||
ok = 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 flags,
|
||||
char *(*fun)(char *, void *), void *dat)
|
||||
transform_name_fp (char **pinput, int type,
|
||||
char const *(*fun) (char const *, int))
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
transform_name (char **pinput, int type)
|
||||
{
|
||||
return transform_name_fp (pinput, type, NULL, NULL);
|
||||
return transform_name_fp (pinput, type, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
80
src/unlink.c
80
src/unlink.c
@ -1,6 +1,6 @@
|
||||
/* Unlink files.
|
||||
|
||||
Copyright 2009-2023 Free Software Foundation, Inc.
|
||||
Copyright 2009-2026 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 */
|
||||
int dir_idx; /* Directory index in wd */
|
||||
idx_t 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,35 +32,25 @@ struct deferred_unlink
|
||||
entry got added to the queue */
|
||||
};
|
||||
|
||||
#define IS_CWD(p) \
|
||||
((p)->is_dir \
|
||||
&& ((p)->file_name[0] == 0 || strcmp ((p)->file_name, ".") == 0))
|
||||
static bool
|
||||
is_cwd (struct deferred_unlink const *p)
|
||||
{
|
||||
return p->is_dir && !p->file_name[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;
|
||||
if (dunlink_avail)
|
||||
{
|
||||
p = dunlink_avail;
|
||||
dunlink_avail = p->next;
|
||||
p->next = NULL;
|
||||
}
|
||||
else
|
||||
p = xmalloc (sizeof (*p));
|
||||
struct deferred_unlink *p = dunlink_avail;
|
||||
if (!p)
|
||||
return xmalloc (sizeof *p);
|
||||
dunlink_avail = p->next;
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -72,14 +62,13 @@ 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
|
||||
@ -94,21 +83,21 @@ static void
|
||||
flush_deferred_unlinks (bool force)
|
||||
{
|
||||
struct deferred_unlink *p, *prev = NULL;
|
||||
int saved_chdir = chdir_current;
|
||||
|
||||
idx_t saved_chdir = chdir_current;
|
||||
|
||||
for (p = dunlink_head; p; )
|
||||
{
|
||||
struct deferred_unlink *next = p->next;
|
||||
|
||||
if (force
|
||||
|| records_written > p->records_written + deferred_unlink_delay)
|
||||
|| p->records_written < records_written)
|
||||
{
|
||||
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;
|
||||
@ -117,7 +106,10 @@ flush_deferred_unlinks (bool force)
|
||||
else
|
||||
fname = p->file_name;
|
||||
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
|
||||
struct fdbase f = fdbase (fname);
|
||||
if (f.fd != BADFD && unlinkat (f.fd, f.base, AT_REMOVEDIR) == 0)
|
||||
fdbase_clear ();
|
||||
else
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
@ -143,11 +135,13 @@ flush_deferred_unlinks (bool force)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
|
||||
struct fdbase f = fdbase (p->file_name);
|
||||
if (f.fd != BADFD && unlinkat (f.fd, f.base, 0) == 0)
|
||||
fdbase_clear ();
|
||||
else if (errno != ENOENT)
|
||||
unlink_error (p->file_name);
|
||||
}
|
||||
dunlink_reclaim (p);
|
||||
dunlink_count--;
|
||||
p = next;
|
||||
if (prev)
|
||||
prev->next = p;
|
||||
@ -170,7 +164,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);
|
||||
@ -178,18 +172,18 @@ flush_deferred_unlinks (bool force)
|
||||
else
|
||||
fname = p->file_name;
|
||||
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
rmdir_error (fname);
|
||||
}
|
||||
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);
|
||||
|
||||
dunlink_reclaim (p);
|
||||
dunlink_count--;
|
||||
p = next;
|
||||
}
|
||||
dunlink_head = dunlink_tail = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
chdir_do (saved_chdir);
|
||||
}
|
||||
|
||||
@ -197,7 +191,7 @@ void
|
||||
finish_deferred_unlinks (void)
|
||||
{
|
||||
flush_deferred_unlinks (true);
|
||||
|
||||
|
||||
while (dunlink_avail)
|
||||
{
|
||||
struct deferred_unlink *next = dunlink_avail->next;
|
||||
@ -212,7 +206,7 @@ queue_deferred_unlink (const char *name, bool is_dir)
|
||||
struct deferred_unlink *p;
|
||||
|
||||
if (dunlink_head
|
||||
&& records_written > dunlink_head->records_written + deferred_unlink_delay)
|
||||
&& records_written > dunlink_head->records_written)
|
||||
flush_deferred_unlinks (false);
|
||||
|
||||
p = dunlink_alloc ();
|
||||
@ -223,11 +217,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);
|
||||
|
||||
49
src/update.c
49
src/update.c
@ -1,6 +1,6 @@
|
||||
/* Update a tar archive.
|
||||
|
||||
Copyright 1988-2023 Free Software Foundation, Inc.
|
||||
Copyright 1988-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -26,10 +26,6 @@
|
||||
#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).
|
||||
@ -49,7 +45,8 @@ static bool acting_as_filter;
|
||||
static void
|
||||
append_file (char *file_name)
|
||||
{
|
||||
int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
|
||||
struct fdbase f = fdbase (file_name);
|
||||
int handle = f.fd == BADFD ? -1 : openat (f.fd, f.base, O_RDONLY | O_BINARY);
|
||||
|
||||
if (handle < 0)
|
||||
{
|
||||
@ -60,23 +57,19 @@ append_file (char *file_name)
|
||||
while (true)
|
||||
{
|
||||
union block *start = find_next_block ();
|
||||
size_t status = full_read (handle, start->buffer,
|
||||
available_space_after (start));
|
||||
if (status == 0)
|
||||
{
|
||||
if (errno == 0)
|
||||
break;
|
||||
read_fatal (file_name);
|
||||
}
|
||||
if (status == SAFE_READ_ERROR)
|
||||
idx_t bufsize = available_space_after (start);
|
||||
idx_t status = full_read (handle, charptr (start), bufsize);
|
||||
if (status < bufsize && errno)
|
||||
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 (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 (close (handle) != 0)
|
||||
if (close (handle) < 0)
|
||||
close_error (file_name);
|
||||
}
|
||||
|
||||
@ -110,7 +103,7 @@ update_archive (void)
|
||||
|
||||
name_gather ();
|
||||
open_archive (ACCESS_UPDATE);
|
||||
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
|
||||
acting_as_filter = streq (archive_name_array[0], "-");
|
||||
xheader_forbid_global ();
|
||||
|
||||
while (!found_end)
|
||||
@ -130,7 +123,7 @@ update_archive (void)
|
||||
struct name *name;
|
||||
|
||||
decode_header (current_header, ¤t_stat_info,
|
||||
¤t_format, 0);
|
||||
¤t_format, false);
|
||||
transform_stat_info (current_header->header.typeflag,
|
||||
¤t_stat_info);
|
||||
archive_format = current_format;
|
||||
@ -145,7 +138,9 @@ update_archive (void)
|
||||
{
|
||||
if (S_ISDIR (s.st_mode))
|
||||
{
|
||||
char *p, *dirp = tar_savedir (current_stat_info.file_name, 1);
|
||||
char *p;
|
||||
char *dirp = tar_savedir (current_stat_info.file_name,
|
||||
true);
|
||||
if (dirp)
|
||||
{
|
||||
namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
|
||||
@ -190,11 +185,11 @@ update_archive (void)
|
||||
switch (previous_status)
|
||||
{
|
||||
case HEADER_STILL_UNREAD:
|
||||
WARN ((0, 0, _("This does not look like a tar archive")));
|
||||
paxwarn (0, _("This does not look like a tar archive"));
|
||||
FALLTHROUGH;
|
||||
case HEADER_SUCCESS:
|
||||
case HEADER_ZERO_BLOCK:
|
||||
ERROR ((0, 0, _("Skipping to next header")));
|
||||
paxerror (0, _("Skipping to next header"));
|
||||
FALLTHROUGH;
|
||||
case HEADER_FAILURE:
|
||||
break;
|
||||
@ -212,7 +207,7 @@ update_archive (void)
|
||||
|
||||
reset_eof ();
|
||||
time_to_start_writing = true;
|
||||
output_start = current_block->buffer;
|
||||
output_start = charptr (current_block);
|
||||
|
||||
{
|
||||
struct name const *p;
|
||||
@ -226,7 +221,7 @@ update_archive (void)
|
||||
if (subcommand_option == CAT_SUBCOMMAND)
|
||||
append_file (file_name);
|
||||
else
|
||||
dump_file (0, file_name, file_name);
|
||||
dump_file (NULL, file_name, file_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/utf8.c
24
src/utf8.c
@ -1,6 +1,6 @@
|
||||
/* Charset handling for GNU tar.
|
||||
|
||||
Copyright 2004-2023 Free Software Foundation, Inc.
|
||||
Copyright 2004-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -35,7 +35,8 @@
|
||||
# define iconv_open(tocode, fromcode) ((iconv_t) -1)
|
||||
|
||||
# undef iconv
|
||||
# define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) (errno = ENOSYS, (size_t) -1)
|
||||
# define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) \
|
||||
(errno = ENOSYS, SIZE_MAX)
|
||||
|
||||
# undef iconv_close
|
||||
# define iconv_close(cd) 0
|
||||
@ -53,14 +54,14 @@ static iconv_t conv_desc[2] = { (iconv_t) -1, (iconv_t) -1 };
|
||||
static iconv_t
|
||||
utf8_init (bool to_utf)
|
||||
{
|
||||
if (conv_desc[(int) to_utf] == (iconv_t) -1)
|
||||
if (conv_desc[to_utf] == (iconv_t) -1)
|
||||
{
|
||||
if (to_utf)
|
||||
conv_desc[(int) to_utf] = iconv_open ("UTF-8", locale_charset ());
|
||||
conv_desc[to_utf] = iconv_open ("UTF-8", locale_charset ());
|
||||
else
|
||||
conv_desc[(int) to_utf] = iconv_open (locale_charset (), "UTF-8");
|
||||
conv_desc[to_utf] = iconv_open (locale_charset (), "UTF-8");
|
||||
}
|
||||
return conv_desc[(int) to_utf];
|
||||
return conv_desc[to_utf];
|
||||
}
|
||||
|
||||
bool
|
||||
@ -72,16 +73,19 @@ utf8_convert (bool to_utf, char const *input, char **output)
|
||||
size_t outlen;
|
||||
iconv_t cd = utf8_init (to_utf);
|
||||
|
||||
if (cd == 0)
|
||||
if (!cd)
|
||||
{
|
||||
*output = xstrdup (input);
|
||||
return true;
|
||||
}
|
||||
else if (cd == (iconv_t)-1)
|
||||
else if (cd == (iconv_t) -1)
|
||||
return false;
|
||||
|
||||
inlen = strlen (input) + 1;
|
||||
outlen = inlen * MB_LEN_MAX + 1;
|
||||
bool overflow = ckd_mul (&outlen, inlen, MB_LEN_MAX);
|
||||
overflow |= ckd_add (&outlen, outlen, 1);
|
||||
if (overflow)
|
||||
xalloc_die ();
|
||||
ob = ret = xmalloc (outlen);
|
||||
ib = (char ICONV_CONST *) input;
|
||||
/* According to POSIX, "if iconv() encounters a character in the input
|
||||
@ -90,7 +94,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 -1.
|
||||
On error, it returns SIZE_MAX.
|
||||
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.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* Warnings for GNU tar.
|
||||
|
||||
Copyright 2009-2023 Free Software Foundation, Inc.
|
||||
Copyright 2009-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -50,6 +50,7 @@ static char const *const warning_args[] = {
|
||||
"failed-read",
|
||||
"missing-zero-blocks",
|
||||
"verbose",
|
||||
"empty-transform",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -81,6 +82,7 @@ static int warning_types[] = {
|
||||
WARN_FAILED_READ,
|
||||
WARN_MISSING_ZERO_BLOCKS,
|
||||
WARN_VERBOSE_WARNINGS,
|
||||
WARN_EMPTY_TRANSFORM
|
||||
};
|
||||
|
||||
ARGMATCH_VERIFY (warning_args, warning_types);
|
||||
@ -90,17 +92,17 @@ int warning_option = WARN_ALL & ~(WARN_VERBOSE_WARNINGS|WARN_MISSING_ZERO_BLOCKS
|
||||
void
|
||||
set_warning_option (const char *arg)
|
||||
{
|
||||
int negate = 0;
|
||||
bool negate = false;
|
||||
int option;
|
||||
|
||||
if (strcmp (arg, "none") == 0)
|
||||
if (streq (arg, "none"))
|
||||
{
|
||||
warning_option = 0;
|
||||
return;
|
||||
}
|
||||
if (strlen (arg) > 2 && memcmp (arg, "no-", 3) == 0)
|
||||
if (strncmp (arg, "no-", 3) == 0)
|
||||
{
|
||||
negate = 1;
|
||||
negate = true;
|
||||
arg += 3;
|
||||
}
|
||||
|
||||
@ -111,3 +113,17 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
308
src/xattrs.c
308
src/xattrs.c
@ -1,6 +1,6 @@
|
||||
/* Support for extended attributes.
|
||||
|
||||
Copyright (C) 2006-2023 Free Software Foundation, Inc.
|
||||
Copyright (C) 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -30,8 +30,8 @@
|
||||
#include "xattr-at.h"
|
||||
#include "selinux-at.h"
|
||||
|
||||
#define XATTRS_PREFIX "SCHILY.xattr."
|
||||
#define XATTRS_PREFIX_LEN (sizeof XATTRS_PREFIX - 1)
|
||||
static char const XATTRS_PREFIX[] = "SCHILY.xattr.";
|
||||
enum { XATTRS_PREFIX_LEN = sizeof XATTRS_PREFIX - 1 };
|
||||
|
||||
void
|
||||
xheader_xattr_init (struct tar_stat_info *st)
|
||||
@ -54,9 +54,7 @@ xattr_map_init (struct xattr_map *map)
|
||||
void
|
||||
xattr_map_free (struct xattr_map *xattr_map)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < xattr_map->xm_size; i++)
|
||||
for (idx_t i = 0; i < xattr_map->xm_size; i++)
|
||||
{
|
||||
free (xattr_map->xm_map[i].xkey);
|
||||
free (xattr_map->xm_map[i].xval_ptr);
|
||||
@ -66,25 +64,23 @@ xattr_map_free (struct xattr_map *xattr_map)
|
||||
|
||||
void
|
||||
xattr_map_add (struct xattr_map *map,
|
||||
const char *key, const char *val, size_t len)
|
||||
const char *key, const char *val, idx_t len)
|
||||
{
|
||||
struct xattr_array *p;
|
||||
|
||||
if (map->xm_size == map->xm_max)
|
||||
map->xm_map = x2nrealloc (map->xm_map, &map->xm_max,
|
||||
sizeof (map->xm_map[0]));
|
||||
p = &map->xm_map[map->xm_size];
|
||||
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];
|
||||
p->xkey = xstrdup (key);
|
||||
p->xval_ptr = xmemdup (val, len + 1);
|
||||
p->xval_ptr = ximemdup (val, len + 1);
|
||||
p->xval_len = len;
|
||||
map->xm_size++;
|
||||
}
|
||||
|
||||
void
|
||||
MAYBE_UNUSED static void
|
||||
xheader_xattr_add (struct tar_stat_info *st,
|
||||
const char *key, const char *val, size_t len)
|
||||
const char *key, const char *val, idx_t len)
|
||||
{
|
||||
size_t klen = strlen (key);
|
||||
idx_t klen = strlen (key);
|
||||
char *xkey = xmalloc (XATTRS_PREFIX_LEN + klen + 1);
|
||||
char *tmp = xkey;
|
||||
|
||||
@ -99,9 +95,7 @@ xheader_xattr_add (struct tar_stat_info *st,
|
||||
void
|
||||
xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < src->xm_size; i++)
|
||||
for (idx_t 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);
|
||||
@ -110,8 +104,8 @@ xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
|
||||
struct xattrs_mask_map
|
||||
{
|
||||
const char **masks;
|
||||
size_t size;
|
||||
size_t used;
|
||||
idx_t size;
|
||||
idx_t used;
|
||||
};
|
||||
|
||||
/* list of fnmatch patterns */
|
||||
@ -260,7 +254,7 @@ fixup_extra_acl_fields (char *ptr)
|
||||
while (*src)
|
||||
{
|
||||
const char *old = src;
|
||||
size_t len = 0;
|
||||
idx_t len = 0;
|
||||
|
||||
src = skip_to_ext_fields (src);
|
||||
len = src - old;
|
||||
@ -284,7 +278,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, int type,
|
||||
char const *file_name, acl_type_t type,
|
||||
char *ptr, bool def)
|
||||
{
|
||||
acl_t acl;
|
||||
@ -299,12 +293,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. */
|
||||
if (acl_delete_def_file_at (chdir_fd, file_name))
|
||||
WARNOPT (WARN_XATTR_WRITE,
|
||||
(0, errno,
|
||||
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,
|
||||
_("acl_delete_def_file_at: Cannot drop default POSIX ACLs "
|
||||
"for file '%s'"),
|
||||
file_name));
|
||||
quote (file_name));
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -316,12 +310,12 @@ xattrs__acls_set (struct tar_stat_info const *st,
|
||||
return;
|
||||
}
|
||||
|
||||
if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
|
||||
struct fdbase f = fdbase (file_name);
|
||||
if (f.fd == BADFD || acl_set_file_at (f.fd, f.base, type, acl) < 0)
|
||||
/* warn even if filesystem does not support acls */
|
||||
WARNOPT (WARN_XATTR_WRITE,
|
||||
(0, errno,
|
||||
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
|
||||
file_name));
|
||||
warnopt (WARN_XATTR_WRITE, errno,
|
||||
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
|
||||
quote (file_name));
|
||||
|
||||
acl_free (acl);
|
||||
}
|
||||
@ -329,7 +323,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, size_t *plen)
|
||||
xattrs_acls_cleanup (char *val, idx_t *plen)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
@ -352,7 +346,7 @@ xattrs_acls_cleanup (char *val, size_t *plen)
|
||||
|
||||
static void
|
||||
acls_get_text (int parentfd, const char *file_name, acl_type_t type,
|
||||
char **ret_ptr, size_t * ret_len)
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
char *val = NULL;
|
||||
acl_t acl;
|
||||
@ -370,11 +364,12 @@ 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 int warned;
|
||||
static bool warned;
|
||||
if (!warned)
|
||||
{
|
||||
WARN ((0, 0, _("--numeric-owner is ignored for ACLs: libacl is not available")));
|
||||
warned = 1;
|
||||
warned = true;
|
||||
paxwarn (0, _("--numeric-owner is ignored for ACLs:"
|
||||
" libacl is not available"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -395,7 +390,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, size_t *ret_len)
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
acls_get_text (parentfd, file_name, ACL_TYPE_ACCESS, ret_ptr, ret_len);
|
||||
}
|
||||
@ -403,7 +398,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, size_t * ret_len)
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
acls_get_text (parentfd, file_name, ACL_TYPE_DEFAULT, ret_ptr, ret_len);
|
||||
}
|
||||
@ -411,13 +406,13 @@ xattrs__acls_get_d (int parentfd, char const *file_name,
|
||||
|
||||
static void
|
||||
acls_one_line (const char *prefix, char delim,
|
||||
const char *aclstring, size_t len)
|
||||
const char *aclstring, idx_t len)
|
||||
{
|
||||
/* support both long and short text representation of posix acls */
|
||||
struct obstack stk;
|
||||
int pref_len = strlen (prefix);
|
||||
idx_t pref_len = strlen (prefix);
|
||||
const char *oldstring = aclstring;
|
||||
int pos = 0;
|
||||
idx_t pos = 0;
|
||||
|
||||
if (!aclstring || !len)
|
||||
return;
|
||||
@ -425,7 +420,7 @@ acls_one_line (const char *prefix, char delim,
|
||||
obstack_init (&stk);
|
||||
while (pos <= len)
|
||||
{
|
||||
int move = strcspn (aclstring, ",\n");
|
||||
idx_t move = strcspn (aclstring, ",\n");
|
||||
if (!move)
|
||||
break;
|
||||
|
||||
@ -441,27 +436,30 @@ acls_one_line (const char *prefix, char delim,
|
||||
|
||||
obstack_1grow (&stk, '\0');
|
||||
|
||||
fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
|
||||
fputs (obstack_finish (&stk), stdlis);
|
||||
|
||||
obstack_free (&stk, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
xattrs_acls_get (int parentfd, char const *file_name,
|
||||
struct tar_stat_info *st, int xisfile)
|
||||
xattrs_acls_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
|
||||
MAYBE_UNUSED struct tar_stat_info *st,
|
||||
MAYBE_UNUSED bool xisfile)
|
||||
{
|
||||
if (acls_option > 0)
|
||||
{
|
||||
#ifndef HAVE_POSIX_ACLS
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("POSIX ACL support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("POSIX ACL support is not available"));
|
||||
}
|
||||
#else
|
||||
int err = file_has_acl_at (parentfd, file_name, &st->stat);
|
||||
if (err == 0)
|
||||
return;
|
||||
if (err == -1)
|
||||
if (err < 0)
|
||||
{
|
||||
call_arg_warn ("file_has_acl_at", file_name);
|
||||
return;
|
||||
@ -477,16 +475,18 @@ xattrs_acls_get (int parentfd, char const *file_name,
|
||||
}
|
||||
|
||||
void
|
||||
xattrs_acls_set (struct tar_stat_info const *st,
|
||||
char const *file_name, char typeflag)
|
||||
xattrs_acls_set (MAYBE_UNUSED struct tar_stat_info const *st,
|
||||
MAYBE_UNUSED char const *file_name, char typeflag)
|
||||
{
|
||||
if (acls_option > 0 && typeflag != SYMTYPE)
|
||||
{
|
||||
#ifndef HAVE_POSIX_ACLS
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("POSIX ACL support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("POSIX ACL support is not available"));
|
||||
}
|
||||
#else
|
||||
xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
|
||||
st->acls_a_ptr, false);
|
||||
@ -501,11 +501,7 @@ static void
|
||||
mask_map_realloc (struct xattrs_mask_map *map)
|
||||
{
|
||||
if (map->used == map->size)
|
||||
{
|
||||
if (map->size == 0)
|
||||
map->size = 4;
|
||||
map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
|
||||
}
|
||||
map->masks = xpalloc (map->masks, &map->size, 1, -1, sizeof *map->masks);
|
||||
}
|
||||
|
||||
void
|
||||
@ -520,20 +516,6 @@ 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)
|
||||
@ -541,59 +523,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 (int parentfd, char const *file_name,
|
||||
struct tar_stat_info *st, int fd)
|
||||
xattrs_xattrs_get (MAYBE_UNUSED int parentfd,
|
||||
MAYBE_UNUSED char const *file_name,
|
||||
MAYBE_UNUSED struct tar_stat_info *st, MAYBE_UNUSED int fd)
|
||||
{
|
||||
if (xattrs_option > 0)
|
||||
if (xattrs_option)
|
||||
{
|
||||
#ifndef HAVE_XATTRS
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("XATTR support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("XATTR support is not available"));
|
||||
}
|
||||
#else
|
||||
static size_t xsz = 1024;
|
||||
static idx_t xsz = 1024 / 2 * 3;
|
||||
static char *xatrs = NULL;
|
||||
ssize_t xret = -1;
|
||||
ssize_t xret;
|
||||
|
||||
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))
|
||||
while (!xatrs
|
||||
|| (((xret = (fd == 0
|
||||
? listxattrat (parentfd, file_name, xatrs, xsz)
|
||||
: flistxattr (fd, xatrs, xsz)))
|
||||
< 0)
|
||||
&& errno == ERANGE))
|
||||
{
|
||||
xatrs = x2nrealloc (xatrs, &xsz, 1);
|
||||
xatrs = xpalloc (xatrs, &xsz, 1, -1, sizeof *xatrs);
|
||||
}
|
||||
|
||||
if (xret == -1)
|
||||
if (xret < 0)
|
||||
call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
|
||||
else
|
||||
{
|
||||
const char *attr = xatrs;
|
||||
static size_t asz = 1024;
|
||||
static idx_t asz = 1024 / 2 * 3;
|
||||
static char *val = NULL;
|
||||
|
||||
if (!val)
|
||||
val = x2nrealloc (val, &asz, 1);
|
||||
|
||||
while (xret > 0)
|
||||
{
|
||||
size_t len = strlen (attr);
|
||||
idx_t len = strlen (attr);
|
||||
ssize_t aret = 0;
|
||||
|
||||
while (((fd == 0)
|
||||
? ((aret = lgetxattrat (parentfd, file_name, attr,
|
||||
val, asz)) == -1)
|
||||
: ((aret = fgetxattr (fd, attr, val, asz)) == -1))
|
||||
&& (errno == ERANGE))
|
||||
while (!val
|
||||
|| (((aret = (fd == 0
|
||||
? lgetxattrat (parentfd, file_name, attr,
|
||||
val, asz)
|
||||
: fgetxattr (fd, attr, val, asz)))
|
||||
< 0)
|
||||
&& errno == ERANGE))
|
||||
{
|
||||
val = x2nrealloc (val, &asz, 1);
|
||||
val = xpalloc (val, &asz, 1, -1, sizeof *val);
|
||||
}
|
||||
|
||||
if (aret != -1)
|
||||
if (0 <= aret)
|
||||
{
|
||||
if (!xattrs_masked_out (attr, true))
|
||||
xheader_xattr_add (st, attr, val, aret);
|
||||
@ -613,26 +595,28 @@ xattrs_xattrs_get (int parentfd, char const *file_name,
|
||||
#ifdef HAVE_XATTRS
|
||||
static void
|
||||
xattrs__fd_set (char const *file_name, char typeflag,
|
||||
const char *attr, const char *ptr, size_t len)
|
||||
const char *attr, const char *ptr, idx_t len)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
const char *sysname = "setxattrat";
|
||||
int ret = -1;
|
||||
int ret;
|
||||
struct fdbase f = fdbase (file_name);
|
||||
|
||||
if (typeflag != SYMTYPE)
|
||||
ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
|
||||
if (f.fd == BADFD)
|
||||
ret = -1;
|
||||
else if (typeflag != SYMTYPE)
|
||||
ret = setxattrat (f.fd, f.base, attr, ptr, len, 0);
|
||||
else
|
||||
{
|
||||
sysname = "lsetxattr";
|
||||
ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
|
||||
ret = lsetxattrat (f.fd, f.base, attr, ptr, len, 0);
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
WARNOPT (WARN_XATTR_WRITE,
|
||||
(0, errno,
|
||||
_("%s: Cannot set '%s' extended attribute for file '%s'"),
|
||||
sysname, attr, file_name));
|
||||
if (ret < 0)
|
||||
warnopt (WARN_XATTR_WRITE, errno,
|
||||
_("%s: Cannot set '%s' extended attribute for file '%s'"),
|
||||
sysname, attr, quote (file_name));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -640,38 +624,42 @@ 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 (int parentfd, char const *file_name,
|
||||
struct tar_stat_info *st, int fd)
|
||||
xattrs_selinux_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
|
||||
MAYBE_UNUSED struct tar_stat_info *st, MAYBE_UNUSED int fd)
|
||||
{
|
||||
if (selinux_context_option > 0)
|
||||
{
|
||||
#if HAVE_SELINUX_SELINUX_H != 1
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("SELinux support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("SELinux support is not available"));
|
||||
}
|
||||
#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 == -1 && errno != ENODATA && errno != ENOTSUP)
|
||||
if (result < 0 && errno != ENODATA && errno != ENOTSUP)
|
||||
call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xattrs_selinux_set (struct tar_stat_info const *st,
|
||||
char const *file_name, char typeflag)
|
||||
xattrs_selinux_set (MAYBE_UNUSED struct tar_stat_info const *st,
|
||||
MAYBE_UNUSED char const *file_name, MAYBE_UNUSED char typeflag)
|
||||
{
|
||||
if (selinux_context_option > 0)
|
||||
{
|
||||
#if HAVE_SELINUX_SELINUX_H != 1
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("SELinux support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("SELinux support is not available"));
|
||||
}
|
||||
#else
|
||||
const char *sysname = "setfilecon";
|
||||
int ret;
|
||||
@ -679,22 +667,24 @@ xattrs_selinux_set (struct tar_stat_info const *st,
|
||||
if (!st->cntx_name)
|
||||
return;
|
||||
|
||||
if (typeflag != SYMTYPE)
|
||||
struct fdbase f = fdbase (file_name);
|
||||
if (f.fd == BADFD)
|
||||
ret = -1;
|
||||
else if (typeflag != SYMTYPE)
|
||||
{
|
||||
ret = setfileconat (chdir_fd, file_name, st->cntx_name);
|
||||
ret = setfileconat (f.fd, f.base, st->cntx_name);
|
||||
sysname = "setfileconat";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
|
||||
ret = lsetfileconat (f.fd, f.base, st->cntx_name);
|
||||
sysname = "lsetfileconat";
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
WARNOPT (WARN_XATTR_WRITE,
|
||||
(0, errno,
|
||||
_("%s: Cannot set SELinux context for file '%s'"),
|
||||
sysname, file_name));
|
||||
if (ret < 0)
|
||||
warnopt (WARN_XATTR_WRITE, errno,
|
||||
_("%s: Cannot set SELinux context for file '%s'"),
|
||||
sysname, quote (file_name));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -702,23 +692,20 @@ xattrs_selinux_set (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 (i = 0; i < mm->used; i++)
|
||||
for (idx_t 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)
|
||||
@ -744,23 +731,24 @@ xattrs_masked_out (const char *kw, bool archiving)
|
||||
}
|
||||
|
||||
void
|
||||
xattrs_xattrs_set (struct tar_stat_info const *st,
|
||||
char const *file_name, char typeflag, int later_run)
|
||||
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)
|
||||
{
|
||||
if (xattrs_option > 0)
|
||||
if (xattrs_option)
|
||||
{
|
||||
#ifndef HAVE_XATTRS
|
||||
static int done = 0;
|
||||
static bool done;
|
||||
if (!done)
|
||||
WARN ((0, 0, _("XATTR support is not available")));
|
||||
done = 1;
|
||||
{
|
||||
done = true;
|
||||
paxwarn (0, _("XATTR support is not available"));
|
||||
}
|
||||
#else
|
||||
size_t i;
|
||||
|
||||
if (!st->xattr_map.xm_size)
|
||||
return;
|
||||
|
||||
for (i = 0; i < st->xattr_map.xm_size; i++)
|
||||
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
|
||||
{
|
||||
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
|
||||
|
||||
@ -771,7 +759,7 @@ xattrs_xattrs_set (struct tar_stat_info const *st,
|
||||
the first run except 'security.capability' which is restored in
|
||||
'later_run == 1'. */
|
||||
if (typeflag == REGTYPE
|
||||
&& later_run == !!strcmp (keyword, "security.capability"))
|
||||
&& streq (keyword, "security.capability") != later_run)
|
||||
continue;
|
||||
|
||||
if (xattrs_masked_out (keyword, false /* extracting */ ))
|
||||
@ -789,23 +777,21 @@ xattrs_xattrs_set (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 > 0 || selinux_context_option > 0 || acls_option > 0)
|
||||
if (xattrs_option || selinux_context_option > 0 || acls_option > 0)
|
||||
{
|
||||
/* placeholders */
|
||||
*output = ' ';
|
||||
output[1] = 0;
|
||||
}
|
||||
|
||||
if (xattrs_option > 0 && st->xattr_map.xm_size)
|
||||
for (i = 0; i < st->xattr_map.xm_size; ++i)
|
||||
if (xattrs_option && st->xattr_map.xm_size)
|
||||
for (idx_t 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 */ ))
|
||||
@ -844,16 +830,14 @@ xattrs_print (struct tar_stat_info const *st)
|
||||
}
|
||||
|
||||
/* xattrs */
|
||||
if (xattrs_option > 0 && st->xattr_map.xm_size)
|
||||
if (xattrs_option && st->xattr_map.xm_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < st->xattr_map.xm_size; ++i)
|
||||
for (idx_t 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: %lu %s\n",
|
||||
(unsigned long) st->xattr_map.xm_map[i].xval_len, keyword);
|
||||
fprintf (stdlis, " x: %td %s\n",
|
||||
st->xattr_map.xm_map[i].xval_len, keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* Support for extended attributes.
|
||||
|
||||
Copyright (C) 2006-2023 Free Software Foundation, Inc.
|
||||
Copyright (C) 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -26,11 +26,8 @@
|
||||
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, int xisfile);
|
||||
struct tar_stat_info *st, bool 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,
|
||||
@ -42,7 +39,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,
|
||||
int later_run);
|
||||
bool 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);
|
||||
|
||||
409
src/xheader.c
409
src/xheader.c
@ -1,6 +1,6 @@
|
||||
/* POSIX extended headers for tar.
|
||||
|
||||
Copyright (C) 2003-2023 Free Software Foundation, Inc.
|
||||
Copyright (C) 2003-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include <system.h>
|
||||
|
||||
#include <c-ctype.h>
|
||||
#include <fnmatch.h>
|
||||
#include <hash.h>
|
||||
#include <inttostr.h>
|
||||
@ -35,7 +36,7 @@ static void code_string (char const *string, char const *keyword,
|
||||
struct xheader *xhdr);
|
||||
|
||||
/* Number of global headers written so far. */
|
||||
static size_t global_header_count;
|
||||
static intmax_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
|
||||
@ -51,7 +52,7 @@ static size_t global_header_count;
|
||||
/* Interface functions to obstacks */
|
||||
|
||||
static void
|
||||
x_obstack_grow (struct xheader *xhdr, const char *ptr, size_t length)
|
||||
x_obstack_grow (struct xheader *xhdr, const char *ptr, idx_t length)
|
||||
{
|
||||
obstack_grow (xhdr->stk, ptr, length);
|
||||
xhdr->size += length;
|
||||
@ -65,7 +66,7 @@ x_obstack_1grow (struct xheader *xhdr, char c)
|
||||
}
|
||||
|
||||
static void
|
||||
x_obstack_blank (struct xheader *xhdr, size_t length)
|
||||
x_obstack_blank (struct xheader *xhdr, idx_t length)
|
||||
{
|
||||
obstack_blank (xhdr->stk, length);
|
||||
xhdr->size += length;
|
||||
@ -123,7 +124,7 @@ xheader_keyword_override_p (const char *keyword)
|
||||
struct keyword_list *kp;
|
||||
|
||||
for (kp = keyword_override_list; kp; kp = kp->next)
|
||||
if (strcmp (kp->pattern, keyword) == 0)
|
||||
if (streq (kp->pattern, keyword))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -160,7 +161,7 @@ xheader_list_destroy (struct keyword_list **root)
|
||||
static _Noreturn void
|
||||
xheader_set_single_keyword (char *kw)
|
||||
{
|
||||
USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet implemented"), kw));
|
||||
paxusage (_("Keyword %s is unknown or not yet implemented"), kw);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -169,7 +170,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)
|
||||
ERROR ((0, 0, _("Time stamp is out of allowed range")));
|
||||
paxerror (0, _("Time stamp is out of allowed range"));
|
||||
else
|
||||
{
|
||||
*tval = t.tv_sec;
|
||||
@ -184,7 +185,7 @@ xheader_set_keyword_equal (char *kw, char *eq)
|
||||
char *p = eq;
|
||||
|
||||
if (eq == kw)
|
||||
USAGE_ERROR ((0, 0, _("Malformed pax option: %s"), quote (kw)));
|
||||
paxusage (_("Malformed pax option: %s"), quote (kw));
|
||||
|
||||
if (eq[-1] == ':')
|
||||
{
|
||||
@ -192,32 +193,32 @@ xheader_set_keyword_equal (char *kw, char *eq)
|
||||
global = false;
|
||||
}
|
||||
|
||||
while (p > kw && isspace ((unsigned char) *p))
|
||||
while (p > kw && c_isspace (*p))
|
||||
p--;
|
||||
|
||||
*p = 0;
|
||||
|
||||
for (p = eq + 1; *p && isspace ((unsigned char) *p); p++)
|
||||
for (p = eq + 1; *p && c_isspace (*p); p++)
|
||||
;
|
||||
|
||||
if (strcmp (kw, "delete") == 0)
|
||||
if (streq (kw, "delete"))
|
||||
{
|
||||
if (xheader_protected_pattern_p (p))
|
||||
USAGE_ERROR ((0, 0, _("Pattern %s cannot be used"), quote (p)));
|
||||
paxusage (_("Pattern %s cannot be used"), quote (p));
|
||||
xheader_list_append (&keyword_pattern_list, p, NULL);
|
||||
}
|
||||
else if (strcmp (kw, "exthdr.name") == 0)
|
||||
else if (streq (kw, "exthdr.name"))
|
||||
assign_string (&exthdr_name, p);
|
||||
else if (strcmp (kw, "globexthdr.name") == 0)
|
||||
else if (streq (kw, "globexthdr.name"))
|
||||
assign_string (&globexthdr_name, p);
|
||||
else if (strcmp (kw, "exthdr.mtime") == 0)
|
||||
else if (streq (kw, "exthdr.mtime"))
|
||||
assign_time_option (&exthdr_mtime_option, &exthdr_mtime, p);
|
||||
else if (strcmp (kw, "globexthdr.mtime") == 0)
|
||||
else if (streq (kw, "globexthdr.mtime"))
|
||||
assign_time_option (&globexthdr_mtime_option, &globexthdr_mtime, p);
|
||||
else
|
||||
{
|
||||
if (xheader_protected_keyword_p (kw))
|
||||
USAGE_ERROR ((0, 0, _("Keyword %s cannot be overridden"), kw));
|
||||
paxusage (_("Keyword %s cannot be overridden"), kw);
|
||||
if (global)
|
||||
xheader_list_append (&keyword_global_override_list, kw, p);
|
||||
else
|
||||
@ -253,10 +254,9 @@ xheader_set_option (char *string)
|
||||
%% A '%' character. */
|
||||
|
||||
char *
|
||||
xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n)
|
||||
xheader_format_name (struct tar_stat_info *st, const char *fmt, intmax_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, size_t n)
|
||||
char *base = NULL;
|
||||
char pidbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char const *pptr = NULL;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char nbuf[INTMAX_STRSIZE_BOUND];
|
||||
char const *nptr = NULL;
|
||||
|
||||
len = 0;
|
||||
idx_t 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, size_t n)
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
nptr = umaxtostr (n, nbuf);
|
||||
nptr = imaxtostr (n, nbuf);
|
||||
len += nbuf + sizeof nbuf - 1 - nptr;
|
||||
break;
|
||||
|
||||
@ -383,14 +383,13 @@ 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));
|
||||
assign_string (&exthdr_name,
|
||||
header_template[pax_file_header][posixly_correct]);
|
||||
return xheader_format_name (st, exthdr_name, 0);
|
||||
}
|
||||
|
||||
@ -399,15 +398,16 @@ xheader_ghdr_name (void)
|
||||
{
|
||||
if (!globexthdr_name)
|
||||
{
|
||||
size_t len;
|
||||
const char *global_header_template = HEADER_TEMPLATE (pax_global_header);
|
||||
const char *global_header_template
|
||||
= header_template[pax_global_header][posixly_correct];
|
||||
const char *tmp = getenv ("TMPDIR");
|
||||
if (!tmp)
|
||||
tmp = "/tmp";
|
||||
len = strlen (tmp) + strlen (global_header_template) + 1;
|
||||
globexthdr_name = xmalloc (len);
|
||||
strcpy(globexthdr_name, tmp);
|
||||
strcat(globexthdr_name, global_header_template);
|
||||
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);
|
||||
}
|
||||
|
||||
return xheader_format_name (NULL, globexthdr_name, global_header_count + 1);
|
||||
@ -416,11 +416,7 @@ xheader_ghdr_name (void)
|
||||
void
|
||||
xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
|
||||
{
|
||||
union block *header;
|
||||
size_t size;
|
||||
char *p;
|
||||
|
||||
size = xhdr->size;
|
||||
idx_t size = xhdr->size;
|
||||
switch (type)
|
||||
{
|
||||
case XGLTYPE:
|
||||
@ -433,21 +429,17 @@ xheader_write (char type, char *name, time_t t, struct xheader *xhdr)
|
||||
t = exthdr_mtime;
|
||||
break;
|
||||
}
|
||||
header = start_private_header (name, size, t);
|
||||
union block *header = start_private_header (name, size, t);
|
||||
header->header.typeflag = type;
|
||||
|
||||
simple_finish_header (header);
|
||||
|
||||
p = xhdr->buffer;
|
||||
char *p = xhdr->buffer;
|
||||
|
||||
do
|
||||
{
|
||||
size_t len;
|
||||
|
||||
header = find_next_block ();
|
||||
len = BLOCKSIZE;
|
||||
if (len > size)
|
||||
len = size;
|
||||
idx_t len = min (size, BLOCKSIZE);
|
||||
memcpy (header->buffer, p, len);
|
||||
if (len < BLOCKSIZE)
|
||||
memset (header->buffer + len, 0, BLOCKSIZE - len);
|
||||
@ -489,7 +481,7 @@ void
|
||||
xheader_forbid_global (void)
|
||||
{
|
||||
if (keyword_global_override_list)
|
||||
USAGE_ERROR ((0, 0, _("can't update global extended header record")));
|
||||
paxusage (_("can't update global extended header record"));
|
||||
}
|
||||
|
||||
/* This is reversal function for xattr_encode_keyword. See comment for
|
||||
@ -532,15 +524,18 @@ xattr_decode_keyword (char *keyword)
|
||||
|
||||
/* General Interface */
|
||||
|
||||
#define XHDR_PROTECTED 0x01
|
||||
#define XHDR_GLOBAL 0x02
|
||||
enum
|
||||
{
|
||||
XHDR_PROTECTED = 0x01,
|
||||
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 *, size_t);
|
||||
void (*decoder) (struct tar_stat_info *, char const *, char const *, idx_t);
|
||||
int flags;
|
||||
bool prefix; /* select handler comparing prefix only */
|
||||
};
|
||||
@ -560,13 +555,13 @@ locate_handler (char const *keyword)
|
||||
for (p = xhdr_tab; p->keyword; p++)
|
||||
if (p->prefix)
|
||||
{
|
||||
size_t kwlen = strlen (p->keyword);
|
||||
idx_t kwlen = strlen (p->keyword);
|
||||
if (strncmp (p->keyword, keyword, kwlen) == 0 && keyword[kwlen] == '.')
|
||||
return p;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp (p->keyword, keyword) == 0)
|
||||
if (streq (p->keyword, keyword))
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -592,7 +587,7 @@ xheader_protected_keyword_p (const char *keyword)
|
||||
|
||||
for (p = xhdr_tab; p->keyword; p++)
|
||||
if (!p->prefix && (p->flags & XHDR_PROTECTED)
|
||||
&& strcmp (p->keyword, keyword) == 0)
|
||||
&& streq (p->keyword, keyword))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -602,34 +597,37 @@ xheader_protected_keyword_p (const char *keyword)
|
||||
static bool
|
||||
decode_record (struct xheader *xhdr,
|
||||
char **ptr,
|
||||
void (*handler) (void *, char const *, char const *, size_t),
|
||||
void (*handler) (void *, char const *, char const *, idx_t),
|
||||
void *data)
|
||||
{
|
||||
char *start = *ptr;
|
||||
char *p = start;
|
||||
size_t len;
|
||||
char *len_lim;
|
||||
char const *keyword;
|
||||
char *nextp;
|
||||
size_t len_max = xhdr->buffer + xhdr->size - start;
|
||||
idx_t len_max = xhdr->buffer + xhdr->size - start;
|
||||
|
||||
while (*p == ' ' || *p == '\t')
|
||||
p++;
|
||||
|
||||
if (! ISDIGIT (*p))
|
||||
idx_t len = stoint (p, &len_lim, NULL, 0, IDX_MAX);
|
||||
|
||||
if (len_lim == p)
|
||||
{
|
||||
/* The length is missing.
|
||||
FIXME: Comment why this is diagnosed only if (*p), or change code. */
|
||||
if (*p)
|
||||
ERROR ((0, 0, _("Malformed extended header: missing length")));
|
||||
paxerror (0, _("Malformed extended header: missing length"));
|
||||
return false;
|
||||
}
|
||||
|
||||
len = strtoumax (p, &len_lim, 10);
|
||||
|
||||
if (len_max < len)
|
||||
{
|
||||
int len_len = len_lim - p;
|
||||
ERROR ((0, 0, _("Extended header length %.*s is out of range"),
|
||||
len_len, p));
|
||||
/* 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);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -639,8 +637,7 @@ decode_record (struct xheader *xhdr,
|
||||
continue;
|
||||
if (p == len_lim)
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
_("Malformed extended header: missing blank after length")));
|
||||
paxerror (0, _("Malformed extended header: missing blank after length"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -648,13 +645,13 @@ decode_record (struct xheader *xhdr,
|
||||
p = strchr (p, '=');
|
||||
if (! (p && p < nextp))
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: missing equal sign")));
|
||||
paxerror (0, _("Malformed extended header: missing equal sign"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextp[-1] != '\n')
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: missing newline")));
|
||||
paxerror (0, _("Malformed extended header: missing newline"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -673,12 +670,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, t->keyword, kp->value, strlen (kp->value));
|
||||
t->decoder (st, kp->pattern, kp->value, strlen (kp->value));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decx (void *data, char const *keyword, char const *value, size_t size)
|
||||
decx (void *data, char const *keyword, char const *value, idx_t size)
|
||||
{
|
||||
struct xhdr_tab const *t;
|
||||
struct tar_stat_info *st = data;
|
||||
@ -691,9 +688,9 @@ decx (void *data, char const *keyword, char const *value, size_t size)
|
||||
if (t)
|
||||
t->decoder (st, keyword, value, size);
|
||||
else
|
||||
WARNOPT (WARN_UNKNOWN_KEYWORD,
|
||||
(0, 0, _("Ignoring unknown extended header keyword '%s'"),
|
||||
keyword));
|
||||
warnopt (WARN_UNKNOWN_KEYWORD, 0,
|
||||
_("Ignoring unknown extended header keyword %s"),
|
||||
quotearg_style (shell_escape_always_quoting_style, keyword));
|
||||
}
|
||||
|
||||
void
|
||||
@ -723,7 +720,7 @@ xheader_decode (struct tar_stat_info *st)
|
||||
|
||||
static void
|
||||
decg (void *data, char const *keyword, char const *value,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
struct keyword_list **kwl = data;
|
||||
struct xhdr_tab const *tab = locate_handler (keyword);
|
||||
@ -777,29 +774,26 @@ xheader_store (char const *keyword, struct tar_stat_info *st,
|
||||
void
|
||||
xheader_read (struct xheader *xhdr, union block *p, off_t size)
|
||||
{
|
||||
size_t j = 0;
|
||||
idx_t j = 0;
|
||||
|
||||
if (size < 0)
|
||||
size = 0; /* Already diagnosed. */
|
||||
|
||||
if (SIZE_MAX - BLOCKSIZE <= size)
|
||||
idx_t size_plus_1;
|
||||
if (ckd_add (&size_plus_1, size, BLOCKSIZE + 1))
|
||||
xalloc_die ();
|
||||
size = size_plus_1 - 1;
|
||||
|
||||
size += BLOCKSIZE;
|
||||
xhdr->size = size;
|
||||
xhdr->buffer = xmalloc (size + 1);
|
||||
xhdr->buffer = xmalloc (size_plus_1);
|
||||
xhdr->buffer[size] = '\0';
|
||||
|
||||
do
|
||||
{
|
||||
size_t len = size;
|
||||
|
||||
if (len > BLOCKSIZE)
|
||||
len = BLOCKSIZE;
|
||||
|
||||
if (!p)
|
||||
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
paxfatal (0, _("Unexpected EOF in archive"));
|
||||
|
||||
idx_t len = min (size, BLOCKSIZE);
|
||||
memcpy (&xhdr->buffer[j], p->buffer, len);
|
||||
set_next_block_after (p);
|
||||
|
||||
@ -821,66 +815,55 @@ 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(const char *keyword)
|
||||
xattr_encode_keyword (char const *keyword)
|
||||
{
|
||||
static char *encode_buffer = NULL;
|
||||
static size_t encode_buffer_size = 0;
|
||||
size_t bp; /* keyword/buffer pointers */
|
||||
static idx_t encode_buffer_size = 0;
|
||||
|
||||
if (!encode_buffer)
|
||||
for (idx_t bp = 0; ; )
|
||||
{
|
||||
encode_buffer_size = 256;
|
||||
encode_buffer = xmalloc (encode_buffer_size);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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, size_t vsize)
|
||||
char const *value, idx_t vsize)
|
||||
{
|
||||
size_t p;
|
||||
size_t n = 0;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
idx_t p;
|
||||
idx_t n = 0;
|
||||
char nbuf[INTMAX_STRSIZE_BOUND];
|
||||
char const *np;
|
||||
size_t len, klen;
|
||||
|
||||
keyword = xattr_encode_keyword (keyword);
|
||||
klen = strlen (keyword);
|
||||
len = klen + vsize + 3; /* ' ' + '=' + '\n' */
|
||||
idx_t klen = strlen (keyword);
|
||||
idx_t len = klen + vsize + 3; /* ' ' + '=' + '\n' */
|
||||
|
||||
do
|
||||
{
|
||||
p = n;
|
||||
np = umaxtostr (len + p, nbuf);
|
||||
np = imaxtostr (len + p, nbuf);
|
||||
n = nbuf + sizeof nbuf - 1 - np;
|
||||
}
|
||||
while (n != p);
|
||||
@ -921,7 +904,7 @@ xheader_destroy (struct xheader *xhdr)
|
||||
}
|
||||
else
|
||||
free (xhdr->buffer);
|
||||
xhdr->buffer = 0;
|
||||
xhdr->buffer = NULL;
|
||||
xhdr->size = 0;
|
||||
}
|
||||
|
||||
@ -940,26 +923,24 @@ xheader_string_add (struct xheader *xhdr, char const *s)
|
||||
if (xhdr->buffer)
|
||||
return;
|
||||
xheader_init (xhdr);
|
||||
xhdr->string_length += strlen (s);
|
||||
x_obstack_grow (xhdr, s, strlen (s));
|
||||
idx_t slen = strlen (s);
|
||||
xhdr->string_length += slen;
|
||||
x_obstack_grow (xhdr, s, slen);
|
||||
}
|
||||
|
||||
bool
|
||||
xheader_string_end (struct xheader *xhdr, char const *keyword)
|
||||
{
|
||||
uintmax_t len;
|
||||
uintmax_t p;
|
||||
uintmax_t n = 0;
|
||||
size_t size;
|
||||
idx_t p;
|
||||
idx_t n = 0;
|
||||
char nbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char const *np;
|
||||
char *cp;
|
||||
|
||||
if (xhdr->buffer)
|
||||
return false;
|
||||
xheader_init (xhdr);
|
||||
|
||||
len = strlen (keyword) + xhdr->string_length + 3; /* ' ' + '=' + '\n' */
|
||||
idx_t len = strlen (keyword) + xhdr->string_length + (sizeof " =\n" - 1);
|
||||
|
||||
do
|
||||
{
|
||||
@ -970,18 +951,10 @@ 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');
|
||||
cp = (char*) obstack_next_free (xhdr->stk) - xhdr->string_length - p - 1;
|
||||
char *cp = obstack_next_free (xhdr->stk);
|
||||
cp -= xhdr->string_length + p + 1;
|
||||
memmove (cp + p, cp, xhdr->string_length);
|
||||
cp = stpcpy (cp, np);
|
||||
*cp++ = ' ';
|
||||
@ -997,15 +970,10 @@ 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.). */
|
||||
ERROR ((0, 0, _("Extended header %s=%s is out of range %s..%s"),
|
||||
keyword, value, minval_string, maxval_string));
|
||||
paxerror (0, _("Extended header %s=%s is out of range %jd..%ju"),
|
||||
keyword, quote (value), minval, maxval);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1055,14 +1023,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
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, quote (arg));
|
||||
return false;
|
||||
}
|
||||
if (*arg_lim)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, quote (arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1090,16 +1058,17 @@ decode_signed_num (intmax_t *num, char const *arg,
|
||||
char const *keyword)
|
||||
{
|
||||
char *arg_lim;
|
||||
intmax_t u = strtosysint (arg, &arg_lim, minval, maxval);
|
||||
bool overflow;
|
||||
intmax_t u = stoint (arg, &arg_lim, &overflow, minval, maxval);
|
||||
|
||||
if (errno == EINVAL || *arg_lim)
|
||||
if ((arg_lim == arg) | *arg_lim)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, quote (arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, minval, maxval);
|
||||
return false;
|
||||
@ -1132,7 +1101,7 @@ static void
|
||||
dummy_decoder (MAYBE_UNUSED struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
MAYBE_UNUSED char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1147,7 +1116,7 @@ static void
|
||||
atime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
@ -1166,7 +1135,7 @@ static void
|
||||
gid_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
intmax_t u;
|
||||
if (decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t),
|
||||
@ -1185,7 +1154,7 @@ static void
|
||||
gname_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&st->gname, arg);
|
||||
}
|
||||
@ -1201,7 +1170,7 @@ static void
|
||||
linkpath_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&st->link_name, arg);
|
||||
}
|
||||
@ -1217,7 +1186,7 @@ static void
|
||||
ctime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
@ -1236,7 +1205,7 @@ static void
|
||||
mtime_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (decode_time (&ts, arg, keyword))
|
||||
@ -1266,7 +1235,7 @@ static void
|
||||
path_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
if (! st->sparse_name_done)
|
||||
raw_path_decoder (st, arg);
|
||||
@ -1276,7 +1245,7 @@ static void
|
||||
sparse_path_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
st->sparse_name_done = true;
|
||||
raw_path_decoder (st, arg);
|
||||
@ -1293,7 +1262,7 @@ static void
|
||||
size_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
@ -1312,7 +1281,7 @@ static void
|
||||
uid_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
intmax_t u;
|
||||
if (decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t),
|
||||
@ -1331,7 +1300,7 @@ static void
|
||||
uname_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&st->uname, arg);
|
||||
}
|
||||
@ -1347,7 +1316,7 @@ static void
|
||||
sparse_size_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
@ -1369,7 +1338,7 @@ static void
|
||||
sparse_numblocks_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, SIZE_MAX, keyword))
|
||||
@ -1384,7 +1353,7 @@ static void
|
||||
sparse_offset_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
size_t const *pi = data;
|
||||
idx_t const *pi = data;
|
||||
code_num (st->sparse_map[*pi].offset, keyword, xhdr);
|
||||
}
|
||||
|
||||
@ -1392,7 +1361,7 @@ static void
|
||||
sparse_offset_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
@ -1400,8 +1369,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
|
||||
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
|
||||
"GNU.sparse.offset", arg));
|
||||
paxerror (0, _("Malformed extended header: excess %s=%s"),
|
||||
"GNU.sparse.offset", arg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1409,7 +1378,7 @@ static void
|
||||
sparse_numbytes_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
size_t const *pi = data;
|
||||
idx_t const *pi = data;
|
||||
code_num (st->sparse_map[*pi].numbytes, keyword, xhdr);
|
||||
}
|
||||
|
||||
@ -1417,7 +1386,7 @@ static void
|
||||
sparse_numbytes_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
@ -1425,8 +1394,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
|
||||
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: excess %s=%s"),
|
||||
keyword, arg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1434,35 +1403,28 @@ static void
|
||||
sparse_map_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
int offset = 1;
|
||||
bool offset = true;
|
||||
struct sp_array e;
|
||||
|
||||
st->sparse_map_avail = 0;
|
||||
while (1)
|
||||
while (true)
|
||||
{
|
||||
intmax_t u;
|
||||
char *delim;
|
||||
|
||||
if (!ISDIGIT (*arg))
|
||||
bool overflow;
|
||||
off_t u = stoint (arg, &delim, &overflow, 0, TYPE_MAXIMUM (off_t));
|
||||
if (delim == arg)
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: invalid %s=%s"),
|
||||
keyword, quote (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 (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
@ -1471,7 +1433,7 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
else
|
||||
{
|
||||
e.numbytes = u;
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
{
|
||||
out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
|
||||
return;
|
||||
@ -1480,8 +1442,8 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
st->sparse_map[st->sparse_map_avail++] = e;
|
||||
else
|
||||
{
|
||||
ERROR ((0, 0, _("Malformed extended header: excess %s=%s"),
|
||||
keyword, arg));
|
||||
paxerror (0, _("Malformed extended header: excess %s=%s"),
|
||||
keyword, quote (arg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1492,9 +1454,10 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
break;
|
||||
else if (*delim != ',')
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
_("Malformed extended header: invalid %s: unexpected delimiter %c"),
|
||||
keyword, *delim));
|
||||
paxerror (0,
|
||||
_("Malformed extended header: invalid %s:"
|
||||
" unexpected delimiter %c"),
|
||||
keyword, *delim);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1502,9 +1465,10 @@ sparse_map_decoder (struct tar_stat_info *st,
|
||||
}
|
||||
|
||||
if (!offset)
|
||||
ERROR ((0, 0,
|
||||
_("Malformed extended header: invalid %s: odd number of values"),
|
||||
keyword));
|
||||
paxerror (0,
|
||||
_("Malformed extended header: invalid %s:"
|
||||
" odd number of values"),
|
||||
keyword);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1518,9 +1482,9 @@ static void
|
||||
dumpdir_decoder (struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
size_t size)
|
||||
idx_t size)
|
||||
{
|
||||
st->dumpdir = xmalloc (size);
|
||||
st->dumpdir = ximalloc (size);
|
||||
memcpy (st->dumpdir, arg, size);
|
||||
}
|
||||
|
||||
@ -1536,7 +1500,7 @@ static void
|
||||
volume_label_decoder (MAYBE_UNUSED struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&volume_label, arg);
|
||||
}
|
||||
@ -1553,10 +1517,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 size_t size)
|
||||
char const *arg, MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
continued_file_size = u;
|
||||
}
|
||||
|
||||
@ -1573,10 +1537,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 size_t size)
|
||||
char const *arg, MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword))
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
|
||||
continued_file_offset = u;
|
||||
}
|
||||
|
||||
@ -1584,7 +1548,7 @@ static void
|
||||
volume_filename_decoder (MAYBE_UNUSED struct tar_stat_info *st,
|
||||
MAYBE_UNUSED char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&continued_file_name, arg);
|
||||
}
|
||||
@ -1599,7 +1563,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 size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
decode_string (&st->cntx_name, arg);
|
||||
}
|
||||
@ -1614,7 +1578,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, size_t size)
|
||||
char const *arg, idx_t size)
|
||||
{
|
||||
st->acls_a_ptr = xmemdup (arg, size + 1);
|
||||
st->acls_a_len = size;
|
||||
@ -1630,7 +1594,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,
|
||||
size_t size)
|
||||
idx_t size)
|
||||
{
|
||||
st->acls_d_ptr = xmemdup (arg, size + 1);
|
||||
st->acls_d_len = size;
|
||||
@ -1640,7 +1604,8 @@ static void
|
||||
xattr_coder (struct tar_stat_info const *st, char const *keyword,
|
||||
struct xheader *xhdr, void const *data)
|
||||
{
|
||||
size_t n = *(size_t *)data;
|
||||
idx_t const *idx_data = data;
|
||||
idx_t n = *idx_data;
|
||||
xheader_print_n (xhdr, keyword,
|
||||
st->xattr_map.xm_map[n].xval_ptr,
|
||||
st->xattr_map.xm_map[n].xval_len);
|
||||
@ -1648,7 +1613,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, size_t size)
|
||||
char const *keyword, char const *arg, idx_t size)
|
||||
{
|
||||
char *xkey;
|
||||
|
||||
@ -1673,10 +1638,10 @@ static void
|
||||
sparse_major_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
|
||||
if (decode_num (&u, arg, INTMAX_MAX, keyword))
|
||||
st->sparse_major = u;
|
||||
}
|
||||
|
||||
@ -1691,10 +1656,10 @@ static void
|
||||
sparse_minor_decoder (struct tar_stat_info *st,
|
||||
char const *keyword,
|
||||
char const *arg,
|
||||
MAYBE_UNUSED size_t size)
|
||||
MAYBE_UNUSED idx_t size)
|
||||
{
|
||||
uintmax_t u;
|
||||
if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword))
|
||||
if (decode_num (&u, arg, INTMAX_MAX, keyword))
|
||||
st->sparse_minor = u;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar regression tests.
|
||||
|
||||
# Copyright 1996-2023 Free Software Foundation, Inc.
|
||||
# Copyright 1996-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -45,21 +45,24 @@ $(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\
|
||||
T-nonl.at\
|
||||
T-mult.at\
|
||||
T-nest.at\
|
||||
acls01.at\
|
||||
acls02.at\
|
||||
acls03.at\
|
||||
add-file.at\
|
||||
append.at\
|
||||
append01.at\
|
||||
@ -68,14 +71,15 @@ TESTSUITE_AT = \
|
||||
append04.at\
|
||||
append05.at\
|
||||
backup01.at\
|
||||
chtype.at\
|
||||
comprec.at\
|
||||
comperr.at\
|
||||
capabs_raw01.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\
|
||||
@ -83,6 +87,8 @@ TESTSUITE_AT = \
|
||||
delete05.at\
|
||||
delete06.at\
|
||||
difflink.at\
|
||||
dirrem01.at\
|
||||
dirrem02.at\
|
||||
exclude.at\
|
||||
exclude01.at\
|
||||
exclude02.at\
|
||||
@ -100,6 +106,10 @@ TESTSUITE_AT = \
|
||||
exclude14.at\
|
||||
exclude15.at\
|
||||
exclude16.at\
|
||||
exclude17.at\
|
||||
exclude18.at\
|
||||
exclude19.at\
|
||||
exclude20.at\
|
||||
extrac01.at\
|
||||
extrac02.at\
|
||||
extrac03.at\
|
||||
@ -125,13 +135,17 @@ TESTSUITE_AT = \
|
||||
extrac23.at\
|
||||
extrac24.at\
|
||||
extrac25.at\
|
||||
extrac26.at\
|
||||
extrac27.at\
|
||||
extrac28.at\
|
||||
extrac29.at\
|
||||
extrac30.at\
|
||||
extrac31.at\
|
||||
filerem01.at\
|
||||
filerem02.at\
|
||||
dirrem01.at\
|
||||
dirrem02.at\
|
||||
gzip.at\
|
||||
grow.at\
|
||||
incremental.at\
|
||||
gzip.at\
|
||||
ignfail.at\
|
||||
incr01.at\
|
||||
incr02.at\
|
||||
incr03.at\
|
||||
@ -143,8 +157,8 @@ TESTSUITE_AT = \
|
||||
incr09.at\
|
||||
incr10.at\
|
||||
incr11.at\
|
||||
incremental.at\
|
||||
indexfile.at\
|
||||
ignfail.at\
|
||||
label01.at\
|
||||
label02.at\
|
||||
label03.at\
|
||||
@ -188,22 +202,16 @@ TESTSUITE_AT = \
|
||||
opcomp04.at\
|
||||
opcomp05.at\
|
||||
opcomp06.at\
|
||||
positional01.at\
|
||||
positional02.at\
|
||||
positional03.at\
|
||||
options.at\
|
||||
options02.at\
|
||||
options03.at\
|
||||
owner.at\
|
||||
pipe.at\
|
||||
recurse.at\
|
||||
positional01.at\
|
||||
positional02.at\
|
||||
positional03.at\
|
||||
recurs02.at\
|
||||
rename01.at\
|
||||
rename02.at\
|
||||
rename03.at\
|
||||
rename04.at\
|
||||
rename05.at\
|
||||
rename06.at\
|
||||
recurse.at\
|
||||
remfiles01.at\
|
||||
remfiles02.at\
|
||||
remfiles03.at\
|
||||
@ -226,12 +234,21 @@ 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\
|
||||
shortupd.at\
|
||||
shortrec.at\
|
||||
shortupd.at\
|
||||
sigpipe.at\
|
||||
skipdir.at\
|
||||
sparse01.at\
|
||||
sparse02.at\
|
||||
sparse03.at\
|
||||
@ -247,6 +264,13 @@ 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\
|
||||
@ -255,21 +279,11 @@ TESTSUITE_AT = \
|
||||
update02.at\
|
||||
update03.at\
|
||||
update04.at\
|
||||
volsize.at\
|
||||
volume.at\
|
||||
verbose.at\
|
||||
verify.at\
|
||||
version.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\
|
||||
volsize.at\
|
||||
volume.at\
|
||||
xattr01.at\
|
||||
xattr02.at\
|
||||
xattr03.at\
|
||||
@ -278,12 +292,11 @@ TESTSUITE_AT = \
|
||||
xattr06.at\
|
||||
xattr07.at\
|
||||
xattr08.at\
|
||||
acls01.at\
|
||||
acls02.at\
|
||||
acls03.at\
|
||||
selnx01.at\
|
||||
selacl01.at\
|
||||
capabs_raw01.at
|
||||
xform-h.at\
|
||||
xform01.at\
|
||||
xform02.at\
|
||||
xform03.at\
|
||||
xform04.at
|
||||
|
||||
distclean-local:
|
||||
-rm -rf download
|
||||
@ -291,7 +304,7 @@ distclean-local:
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
|
||||
AUTOTEST = $(AUTOM4TE) --language=autotest
|
||||
$(TESTSUITE): package.m4 $(TESTSUITE_AT)
|
||||
$(TESTSUITE): compress.m4 package.m4 $(TESTSUITE_AT)
|
||||
$(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
@ -332,6 +345,7 @@ AM_CPPFLAGS = \
|
||||
-DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
LDADD = ../gnu/libgnu.a\
|
||||
$(LIB_ACL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)\
|
||||
$(LIB_GETRANDOM) $(LIB_HARD_LOCALE) $(FILE_HAS_ACL_LIB) $(LIB_MBRTOWC)\
|
||||
$(LIB_SELINUX) $(LIB_SETLOCALE_NULL)
|
||||
$(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)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU tar.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2014-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2014-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -43,4 +43,3 @@ dir/file1
|
||||
dir/file2
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2014-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2014-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@ -43,4 +43,3 @@ dir/file1
|
||||
dir/file2
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU tar.
|
||||
#
|
||||
@ -43,4 +43,3 @@ file4
|
||||
],[],[],[],[ustar])
|
||||
|
||||
AT_CLEANUP
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU tar.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU tar.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2006-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file is part of test suite for GNU tar. -*- Autotest -*-
|
||||
# Copyright 2015-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2015-2026 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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU tar.
|
||||
#
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2015-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 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],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2011-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2011-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2011-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2011-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2026 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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2016-2023 Free Software Foundation, Inc.
|
||||
# Copyright 2016-2026 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
Loading…
x
Reference in New Issue
Block a user