Compare commits
415 Commits
release_1_
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e545d446df | ||
|
|
e7a5e12445 | ||
|
|
31f68bbe2a | ||
|
|
10954cf163 | ||
|
|
b3a71dbdb9 | ||
|
|
cf16a23945 | ||
|
|
d6a60bba76 | ||
|
|
2ccd643d01 | ||
|
|
fdff045d4b | ||
|
|
90cceec4bb | ||
|
|
826c5eb64e | ||
|
|
2096772fbe | ||
|
|
c542d3d0c8 | ||
|
|
4c7a3798d8 | ||
|
|
5d6736e394 | ||
|
|
9ee30c9804 | ||
|
|
4695adfd59 | ||
|
|
a484178a18 | ||
|
|
e39b93d822 | ||
|
|
8ccfc4e416 | ||
|
|
e7987b72c6 | ||
|
|
06805b9281 | ||
|
|
5461025569 | ||
|
|
3f2b847ddd | ||
|
|
6af0596726 | ||
|
|
204b414d11 | ||
|
|
6a1581240b | ||
|
|
4f814e0e4c | ||
|
|
4177c98bcc | ||
|
|
5e8a915b16 | ||
|
|
edf38d13a4 | ||
|
|
3da78400ea | ||
|
|
02402920f8 | ||
|
|
021845e54d | ||
|
|
0f289d7238 | ||
|
|
71d1619abd | ||
|
|
719d3b44b7 | ||
|
|
a65f01ac35 | ||
|
|
2cde05fa10 | ||
|
|
e89c7a45eb | ||
|
|
24c8306965 | ||
|
|
ba26ec86e2 | ||
|
|
de64229632 | ||
|
|
17debecd73 | ||
|
|
02f9af1b8d | ||
|
|
87f9e42108 | ||
|
|
0f0722df45 | ||
|
|
7324326b1d | ||
|
|
705bb09317 | ||
|
|
4ba281eca3 | ||
|
|
bc277c7069 | ||
|
|
f8e14746d2 | ||
|
|
35d9845d5d | ||
|
|
0b74885e81 | ||
|
|
258d1c44e5 | ||
|
|
e49537dcdf | ||
|
|
66be5a789e | ||
|
|
badd509078 | ||
|
|
4e9e55fbf9 | ||
|
|
fffc6149fd | ||
|
|
8ed180b03c | ||
|
|
131ceea7a5 | ||
|
|
4986147986 | ||
|
|
e5aac38c80 | ||
|
|
5c4f8cadbd | ||
|
|
496cf61638 | ||
|
|
d935dc7d1c | ||
|
|
9f0e54ab2f | ||
|
|
c7b69f05bc | ||
|
|
4eb9d052b2 | ||
|
|
5209d1dfe0 | ||
|
|
b2ed3caefe | ||
|
|
79d1ac38c1 | ||
|
|
79a442d7b0 | ||
|
|
7a37621e5b | ||
|
|
c1027eb5ae | ||
|
|
bc7e758b27 | ||
|
|
de72aa0cd6 | ||
|
|
eeda008a59 | ||
|
|
5c713540e3 | ||
|
|
88ccec5d6c | ||
|
|
399c08b3bf | ||
|
|
7f8fe36040 | ||
|
|
b846956c60 | ||
|
|
ac119c80e4 | ||
|
|
d43adc97d6 | ||
|
|
1f73735ba0 | ||
|
|
39cd8c6586 | ||
|
|
84a08f530d | ||
|
|
c366383852 | ||
|
|
738de9ecde | ||
|
|
771ca7b686 | ||
|
|
8184adddab | ||
|
|
7868054c03 | ||
|
|
4f3824743f | ||
|
|
c5b86d7672 | ||
|
|
c0bf6a723b | ||
|
|
b44c612f5d | ||
|
|
066f7729e0 | ||
|
|
7958eb97e6 | ||
|
|
66262c10d1 | ||
|
|
a339f05cd2 | ||
|
|
cc8f5f78b2 | ||
|
|
193eb8f81d | ||
|
|
93082d6eb8 | ||
|
|
e5bc23efcc | ||
|
|
12d67f44de | ||
|
|
3730dddb3f | ||
|
|
8e82f367d4 | ||
|
|
66b59fccb1 | ||
|
|
91c031678d | ||
|
|
5227d66b3c | ||
|
|
f51461d8be | ||
|
|
6efa8de409 | ||
|
|
c67d223854 | ||
|
|
55f2a0772e | ||
|
|
daf634c44e | ||
|
|
5f8f129415 | ||
|
|
dc101747e8 | ||
|
|
7fb1b6877f | ||
|
|
f4e2411bf5 | ||
|
|
ace146f6a4 | ||
|
|
df6de51574 | ||
|
|
8378991cba | ||
|
|
fa6d317bc7 | ||
|
|
beeb19f927 | ||
|
|
553210d5ad | ||
|
|
ee2ec5ff19 | ||
|
|
d9d4435692 | ||
|
|
2251317e3f | ||
|
|
972bebf07e | ||
|
|
8e2898ab11 | ||
|
|
0b43ea2906 | ||
|
|
e4d1edadef | ||
|
|
0836a51147 | ||
|
|
afa743ac23 | ||
|
|
01dd89c121 | ||
|
|
1263f9bc1d | ||
|
|
1ff0b63f48 | ||
|
|
34d15af170 | ||
|
|
63712973c7 | ||
|
|
d9ec6f04e2 | ||
|
|
b5418cd393 | ||
|
|
615732a804 | ||
|
|
dd1a6bd37a | ||
|
|
41654f91f0 | ||
|
|
8d90723d30 | ||
|
|
14d8fc718f | ||
|
|
883cc555df | ||
|
|
e1005b385d | ||
|
|
14f00a2c7a | ||
|
|
f122fc94a7 | ||
|
|
b31afe7cf0 | ||
|
|
ea6f84dd40 | ||
|
|
2d3396c3ea | ||
|
|
d70b8b3b39 | ||
|
|
c445d99d4f | ||
|
|
2c9730357f | ||
|
|
ef0f882382 | ||
|
|
66162927eb |
44
.gitignore
vendored
44
.gitignore
vendored
@@ -1,32 +1,32 @@
|
||||
*.a
|
||||
*.[aios]
|
||||
*.[gx]z
|
||||
*.bz2
|
||||
*.cache
|
||||
*.diff
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.orig
|
||||
*.patch
|
||||
*.rej
|
||||
*.so
|
||||
*DISTFILES
|
||||
*~
|
||||
.bootstrap
|
||||
.deps
|
||||
.emacs*
|
||||
.libs
|
||||
.gdbinit
|
||||
ABOUT-NLS
|
||||
ChangeLog
|
||||
INSTALL
|
||||
Make.rules
|
||||
/.bootstrap
|
||||
/ABOUT-NLS
|
||||
/ChangeLog
|
||||
/INSTALL
|
||||
/Make.rules
|
||||
/aclocal.m4
|
||||
/build-aux/
|
||||
/conf*
|
||||
!/configure.ac
|
||||
/gnu/
|
||||
/m4/
|
||||
/rmt/
|
||||
/stamp-h1
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
build-aux
|
||||
build-aux/
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
gnu
|
||||
libtool
|
||||
m4
|
||||
rmt
|
||||
stamp-h1
|
||||
TAGS
|
||||
|
||||
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
|
||||
|
||||
8
COPYING
8
COPYING
@@ -1,7 +1,7 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
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 Free Software Foundation, Inc.
|
||||
Copyright 1989-1997, 2013, 2023-2025 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,7 +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 Free Software Foundation, Inc.
|
||||
Copyright 1997-2001, 2003-2009, 2013, 2023-2025 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -934,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.
|
||||
@@ -4522,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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Main Makefile for GNU tar.
|
||||
|
||||
# Copyright 1994-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
161
NEWS
161
NEWS
@@ -1,5 +1,160 @@
|
||||
GNU tar NEWS - User visible changes. 2019-02-23
|
||||
GNU tar NEWS - User visible changes. 2025-07-26
|
||||
Please send GNU tar bug reports to <bug-tar@gnu.org>
|
||||
|
||||
version TBD
|
||||
|
||||
* 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
|
||||
|
||||
** 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 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.
|
||||
|
||||
|
||||
version 1.35 - Sergey Poznyakoff, 2023-07-18
|
||||
|
||||
* Fail when building GNU tar, if the platform supports 64-bit time_t
|
||||
but the build uses only 32-bit time_t.
|
||||
|
||||
* Leave the devmajor and devminor fields empty (rather than zero) for
|
||||
non-special files, as this is more compatible with traditional tar.
|
||||
|
||||
* Bug fixes
|
||||
|
||||
** Fix interaction of --update with --wildcards.
|
||||
|
||||
** When extracting archives into an empty directory, do not create
|
||||
hard links to files outside that directory.
|
||||
|
||||
** Handle partial reads from regular files.
|
||||
|
||||
** Warn "file changed as we read it" less often.
|
||||
Formerly, tar warned if the file's size or ctime changed.
|
||||
However, this generated a false positive if tar read a file
|
||||
while another process hard-linked to it, changing its ctime.
|
||||
Now, tar warns if the file's size, mtime, user ID, group ID,
|
||||
or mode changes. Although neither heuristic is perfect,
|
||||
the new one should work better in practice.
|
||||
|
||||
** Fix --ignore-failed-read to ignore file-changed read errors
|
||||
as far as exit status is concerned. You can now suppress file-changed
|
||||
issues entirely with --ignore-failed-read --warning=no-file-changed.
|
||||
|
||||
** Fix --remove-files to not remove a file that changed while we read it.
|
||||
|
||||
** Fix --atime-preserve=replace to not fail if there was no need to replace,
|
||||
either because we did not read the file, or the atime did not change.
|
||||
|
||||
** Fix race when creating a parent directory while another process is
|
||||
also doing so.
|
||||
|
||||
** Fix handling of prefix keywords not followed by "." in pax headers.
|
||||
|
||||
** Fix handling of out-of-range sparse entries in pax headers.
|
||||
|
||||
** Fix handling of --transform='s/s/@/2'.
|
||||
|
||||
** Fix treatment of options ending in / in files-from list.
|
||||
|
||||
** Fix crash on 'tar --checkpoint-action exec=\"'.
|
||||
|
||||
** Fix low-memory crash when reading incremental dumps.
|
||||
|
||||
** Fix --exclude-vcs-ignores memory allocation misuse.
|
||||
|
||||
|
||||
version 1.34 - Sergey Poznyakoff, 2021-02-13
|
||||
|
||||
* Fix extraction over pipe (savannah bug #60002)
|
||||
|
||||
* Fix memory leak in read_header (savannah bug #59897)
|
||||
|
||||
* Fix extraction when . and .. are unreadable
|
||||
|
||||
See https://lists.gnu.org/archive/html/bug-tar/2021-01/msg00012.html
|
||||
|
||||
* Gracefully handle duplicate symlinks when extracting
|
||||
|
||||
See https://lists.gnu.org/archive/html/bug-tar/2021-01/msg00026.html
|
||||
|
||||
* Re-initialize supplementary groups when switching to user privileges
|
||||
|
||||
version 1.33 - Sergey Poznyakoff, 2021-01-07
|
||||
|
||||
* POSIX extended format headers do not include PID by default
|
||||
|
||||
The intent is to make binary-equivalent PAX archives easy to create. If
|
||||
POSIXLY_CORRECT is set, the POSIX standard default is used, which embeds
|
||||
the pid.
|
||||
|
||||
* --delay-directory-restore works for archives with reversed member ordering
|
||||
|
||||
* Fix extraction of a symbolic link hardlinked to another symbolic link
|
||||
|
||||
* Wildcards in exclude-vcs-ignore mode don't match slash
|
||||
|
||||
* Fix the --no-overwrite-dir option
|
||||
|
||||
Given this option, previous versions of tar failed to preserve
|
||||
permissions of empty directories and to create files under directories
|
||||
owned by the current user that did not have the S_IWUSR bit set.
|
||||
|
||||
* Fix handling of chained renames in incremental backups
|
||||
|
||||
* Link counting works for file names supplied with -T
|
||||
|
||||
* Accept only position-sensitive (file-selection) options in file list files.
|
||||
|
||||
Using such options as -f, -z, etc. is senseless in a file list file and
|
||||
bypasses option consistency checks in decode_options. Therefore,
|
||||
only options related to file selection (a.k.a position-sensitive options)
|
||||
are allowed in file list files.
|
||||
|
||||
|
||||
version 1.32 - Sergey Poznyakoff, 2019-02-23
|
||||
@@ -1687,7 +1842,7 @@ Versions 1.07 back to 1.00 by Jay Fenlason.
|
||||
|
||||
|
||||
|
||||
Copyright 1994-2019 Free Software Foundation, Inc.
|
||||
Copyright 1994-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -1707,7 +1862,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Local variables:
|
||||
mode: outline
|
||||
paragraph-separate: "[ ]*$"
|
||||
eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
eval: (add-hook 'write-file-functions #'time-stamp nil t)
|
||||
time-stamp-start: "changes. "
|
||||
time-stamp-format: "%:y-%02m-%02d"
|
||||
time-stamp-end: "\n"
|
||||
|
||||
2
README
2
README
@@ -221,7 +221,7 @@ and share your findings by writing to <bug-tar@gnu.org>.
|
||||
|
||||
* Copying
|
||||
|
||||
Copyright 1990-2019 Free Software Foundation, Inc.
|
||||
Copyright 1990-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
32
README-alpha
32
README-alpha
@@ -3,39 +3,9 @@ This is GNU tar.
|
||||
This is a *pre-release* version, and not ready for production use yet.
|
||||
Please send comments and problem reports to <bug-tar@gnu.org>.
|
||||
|
||||
If you have taken the sources from CVS you will need the following
|
||||
packages (or later) to build GNU tar. We don't make any extra effort
|
||||
to accommodate older versions of these packages, so please make sure
|
||||
that you have the latest stable version.
|
||||
|
||||
- Automake <http://www.gnu.org/software/automake/>
|
||||
- Autoconf <http://www.gnu.org/software/autoconf/>
|
||||
- Bison <http://www.gnu.org/software/bison/>
|
||||
- Gettext <http://www.gnu.org/software/gettext/>
|
||||
- Gzip <http://www.gnu.org/software/gzip/>
|
||||
- M4 <http://www.gnu.org/software/m4/>
|
||||
- Texinfo <http://www.gnu.org/software/texinfo>
|
||||
- Wget <http://www.gnu.org/software/wget/>
|
||||
|
||||
As of this writing, the latest stable version of Gzip is 1.2.4 but we
|
||||
suggest using test version 1.3.5 (or later, if one becomes available).
|
||||
|
||||
Valgrind <http://valgrind.org/> is also highly recommended, if
|
||||
Valgrind supports your architecture.
|
||||
|
||||
Before building the package, run "bootstrap". It will obtain gnulib
|
||||
and paxutils files from their Git repositories on Savannah. Then, it will
|
||||
fetch the po files from tar page at Translation Project, and, finally, it
|
||||
will start autoconfiguration process. Simply running bootstrap without
|
||||
arguments should do in most cases.
|
||||
|
||||
Bootstrap reads its configuration from file bootstrap.conf located on the
|
||||
top of tar source tree. Several options are provided that modify its
|
||||
behavior. Run 'bootstrap --help' for a list.
|
||||
|
||||
|
||||
|
||||
Copyright 2001-2019 Free Software Foundation, Inc.
|
||||
Copyright 2001-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -8,11 +8,22 @@ tar. We do not make any efforts to accommodate older versions of
|
||||
these packages, so please make sure that you have the latest stable
|
||||
version.
|
||||
|
||||
- Automake <http://www.gnu.org/software/automake/>
|
||||
- Autoconf <http://www.gnu.org/software/autoconf/>
|
||||
- Automake <http://www.gnu.org/software/automake/>
|
||||
- Bison <http://www.gnu.org/software/bison/>
|
||||
- M4 <http://www.gnu.org/software/m4/>
|
||||
- Texinfo <http://www.gnu.org/software/texinfo>
|
||||
- Gettext <http://www.gnu.org/software/gettext/>
|
||||
- Git <http://git.or.cz>
|
||||
- Gzip <http://www.gnu.org/software/gzip/>
|
||||
- Texinfo <http://www.gnu.org/software/texinfo>
|
||||
- Wget <http://www.gnu.org/software/wget/>
|
||||
|
||||
Up-to-date compilers and libraries are also recommended, for better
|
||||
static checking. You may be able to use an older compiler by building
|
||||
with 'make WERROR_CFLAGS='; if so, don't worry about its false alarms.
|
||||
|
||||
Valgrind <http://valgrind.org/> is also highly recommended, if
|
||||
Valgrind supports your architecture.
|
||||
|
||||
* Bootstrapping
|
||||
|
||||
@@ -34,12 +45,26 @@ INSTALLATION).
|
||||
Normally you will have to run bootstrap only once. However, if you
|
||||
intend to hack on GNU tar, you might need to run it again later.
|
||||
There are lots of options that you may find useful in this case.
|
||||
See 'bootstrap --help' for a detailed list.
|
||||
See './bootstrap --help' for a detailed list.
|
||||
|
||||
Bootstrapping obtains Gnulib and Paxutils files from their Git
|
||||
repositories on Savannah. Then, it fetches translations from the
|
||||
Translation Project, and, finally, it builds files useful for
|
||||
configuration. Simply running ./bootstrap without arguments should do
|
||||
in most cases.
|
||||
|
||||
The file bootstrap.conf contains bootstrapping configuration.
|
||||
Several options are provided that modify its behavior.
|
||||
Run './bootstrap --help' for a list.
|
||||
|
||||
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'.
|
||||
|
||||
|
||||
* Copyright information
|
||||
|
||||
Copyright 2007-2019 Free Software Foundation, Inc.
|
||||
Copyright 2007-2025 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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2003-2025 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-2019 Free Software Foundation, Inc.
|
||||
dnl Copyright 2009-2025 Free Software Foundation, Inc.
|
||||
dnl
|
||||
dnl This file is part of GNU tar.
|
||||
dnl
|
||||
@@ -21,7 +21,7 @@ AC_DEFUN([TAR_COMPR_PROGRAM],[
|
||||
m4_pushdef([tar_compr_define],translit($1,[a-z+-],[A-ZX_])[_PROGRAM])
|
||||
m4_pushdef([tar_compr_var],[tar_cv_compressor_]translit($1,[+-],[x_]))
|
||||
AC_ARG_WITH($1,
|
||||
AC_HELP_STRING([--with-]$1[=PROG],
|
||||
AS_HELP_STRING([--with-]$1[=PROG],
|
||||
[use PROG as ]$1[ compressor program]),
|
||||
[tar_compr_var=${withval}],
|
||||
[tar_compr_var=m4_if($2,,$1,$2)])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bootstrap configuration for GNU tar.
|
||||
|
||||
# Copyright 2006-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2025 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
|
||||
'
|
||||
|
||||
|
||||
@@ -60,21 +62,19 @@ if [ -r .bootstrap ]; then
|
||||
eval set -- "`sed 's/#.*$//;/^$/d' .bootstrap | tr '\n' ' '` $*"
|
||||
fi
|
||||
|
||||
test -d m4 || mkdir m4
|
||||
test -d $source_base || mkdir $source_base
|
||||
bootstrap_post_pull_hook() {
|
||||
mkdir -p m4 $source_base
|
||||
git submodule init
|
||||
git submodule update
|
||||
}
|
||||
|
||||
test -f ChangeLog || cat > ChangeLog <<EOT
|
||||
This file is a placeholder. It will be replaced with the actual ChangeLog
|
||||
by make dist. Run make ChangeLog if you wish to create it earlier.
|
||||
EOT
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
PAXUTILS=paxutils
|
||||
|
||||
# gnulib modules used by this package.
|
||||
# getopt-gnu is for paxutils.
|
||||
gnulib_modules="$avoided_gnulib_modules
|
||||
`grep -h '^[^#]' gnulib.modules $PAXUTILS/gnulib.modules`
|
||||
`grep -h '^[^#]' gnulib.modules`
|
||||
getopt-gnu
|
||||
"
|
||||
|
||||
# copy_files srcdir dstdir
|
||||
@@ -100,27 +100,30 @@ copy_files() {
|
||||
done
|
||||
}
|
||||
|
||||
# Import from paxutils
|
||||
copy_files ${PAXUTILS} .
|
||||
copy_files ${PAXUTILS}/am m4
|
||||
bootstrap_post_import_hook() {
|
||||
|
||||
echo "$0: Creating m4/paxutils.m4"
|
||||
(echo "# This file is generated automatically. Please, do not edit."
|
||||
echo "#"
|
||||
echo "AC_DEFUN([${package}_PAXUTILS],["
|
||||
cat ${PAXUTILS}/am/DISTFILES | sed '/^#/d;s/\(.*\)\.m4/pu_\1/' | tr a-z A-Z
|
||||
echo "])") > ./m4/paxutils.m4
|
||||
#FIXME ignorefile m4 paxutils.m4
|
||||
test -f ChangeLog || cat > ChangeLog <<EOT
|
||||
This file is a placeholder. It will be replaced with the actual ChangeLog
|
||||
by make dist. Run make ChangeLog if you wish to create it earlier.
|
||||
EOT
|
||||
|
||||
if [ -d rmt ]; then
|
||||
:
|
||||
else
|
||||
mkdir rmt
|
||||
fi
|
||||
# Import from paxutils
|
||||
copy_files ${PAXUTILS} .
|
||||
copy_files ${PAXUTILS}/am m4
|
||||
|
||||
for dir in doc rmt lib tests
|
||||
do
|
||||
copy_files ${PAXUTILS}/$dir $dir
|
||||
done
|
||||
echo "$0: Creating m4/paxutils.m4"
|
||||
(echo "# This file is generated automatically. Please, do not edit."
|
||||
echo "#"
|
||||
echo "AC_DEFUN([${package}_PAXUTILS],["
|
||||
cat ${PAXUTILS}/am/DISTFILES | sed '/^#/d;s/\(.*\)\.m4/pu_\1/' | tr a-z A-Z
|
||||
echo "])") > ./m4/paxutils.m4
|
||||
#FIXME ignorefile m4 paxutils.m4
|
||||
|
||||
copy_files ${PAXUTILS}/paxlib lib pax
|
||||
mkdir -p rmt
|
||||
|
||||
for dir in doc rmt lib tests; do
|
||||
copy_files ${PAXUTILS}/$dir $dir
|
||||
done
|
||||
|
||||
copy_files ${PAXUTILS}/paxlib lib pax
|
||||
}
|
||||
|
||||
104
configure.ac
104
configure.ac
@@ -1,6 +1,6 @@
|
||||
# Configure template for GNU tar. -*- autoconf -*-
|
||||
|
||||
# Copyright 1991, 1994-2010, 2013-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1991, 1994-2010, 2013-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -17,46 +17,37 @@
|
||||
# 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.32], [bug-tar@gnu.org])
|
||||
AC_INIT([GNU tar], [1.35], [bug-tar@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/tar.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_PREREQ([2.63])
|
||||
AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 dist-xz std-options silent-rules])
|
||||
AC_PREREQ([2.71])
|
||||
AM_INIT_AUTOMAKE([1.15 gnits tar-ustar dist-bzip2 dist-xz std-options silent-rules])
|
||||
|
||||
# Enable silent rules by default:
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CC
|
||||
AC_EXEEXT
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_YACC
|
||||
gl_EARLY
|
||||
AC_CHECK_TOOLS([AR], [ar])
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
AC_C_INLINE
|
||||
|
||||
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
|
||||
|
||||
AC_MSG_CHECKING([for st_fstype string in struct stat])
|
||||
AC_CACHE_VAL(diff_cv_st_fstype_string,
|
||||
[AC_TRY_COMPILE([#include <sys/types.h>
|
||||
#include <sys/stat.h>], [struct stat s; s.st_fstype[0] = 'x';],
|
||||
diff_cv_st_fstype_string=yes,
|
||||
diff_cv_st_fstype_string=no)])
|
||||
[AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
]],
|
||||
[[struct stat s; s.st_fstype[0] = 'x';]])],
|
||||
[diff_cv_st_fstype_string=yes],
|
||||
[diff_cv_st_fstype_string=no])])
|
||||
AC_MSG_RESULT($diff_cv_st_fstype_string)
|
||||
if test $diff_cv_st_fstype_string = yes; then
|
||||
AC_DEFINE(HAVE_ST_FSTYPE_STRING, 1,
|
||||
@@ -91,28 +82,45 @@ 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
|
||||
|
||||
# gnulib modules
|
||||
gl_INIT
|
||||
|
||||
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
|
||||
AC_DEFINE([GNULIB_EXCLUDE_SINGLE_THREAD], [1],
|
||||
[Define if all programs in this package call functions of the Gnulib
|
||||
'exclude' module only from a single thread.])
|
||||
AC_DEFINE([GNULIB_MBRTOWC_SINGLE_THREAD], [1],
|
||||
[Define if all programs in this package call functions of the Gnulib
|
||||
'mbtowc' module only from a single thread.])
|
||||
AC_DEFINE([GNULIB_REGEX_SINGLE_THREAD], [1],
|
||||
[Define if all programs in this package call functions of the Gnulib
|
||||
'regex' module only from a single thread.])
|
||||
AC_DEFINE([GNULIB_WCHAR_SINGLE_LOCALE], [1],
|
||||
[Define if all programs in this package call locale-sensitive functions
|
||||
like mbrtowc only after setting the locale, and never change the
|
||||
locale once set.])
|
||||
|
||||
if test $COMPILE_ERROR_C = 1; then
|
||||
# This means that Gnulib's 'error' function will be used. It precedes
|
||||
# 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.
|
||||
AC_DEFINE([ENABLE_ERROR_PRINT_PROGNAME],[1],
|
||||
[Enable the use of error_print_progname to print program name with error messages.
|
||||
See comment to function tar_print_progname in src/tar.c])
|
||||
fi
|
||||
fi
|
||||
|
||||
# paxutils modules
|
||||
tar_PAXUTILS
|
||||
@@ -145,7 +153,7 @@ AC_ARG_ENABLE([gcc-warnings],
|
||||
gl_gcc_warnings=$enableval],
|
||||
[gl_gcc_warnings=no
|
||||
if test -d "$srcdir"/.git; then
|
||||
gl_GCC_VERSION_IFELSE([4], [6], [gl_gcc_warnings=yes])
|
||||
gl_GCC_VERSION_IFELSE([11], [2], [gl_gcc_warnings=yes])
|
||||
fi]
|
||||
)
|
||||
|
||||
@@ -171,12 +179,11 @@ if test "$gl_gcc_warnings" = yes; then
|
||||
done
|
||||
gl_WARN_ADD([-Wno-sign-compare]) # Too many warnings for now
|
||||
gl_WARN_ADD([-Wno-type-limits]) # It's OK to optimize based on types.
|
||||
gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
|
||||
gl_WARN_ADD([-Wno-format-nonliteral])
|
||||
|
||||
|
||||
gl_WARN_ADD([-fdiagnostics-show-option])
|
||||
gl_WARN_ADD([-funit-at-a-time])
|
||||
|
||||
|
||||
|
||||
AC_SUBST([WARN_CFLAGS])
|
||||
|
||||
@@ -218,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,
|
||||
@@ -248,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
|
||||
@@ -344,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]),
|
||||
@@ -356,7 +355,7 @@ AC_CHECK_TYPE(iconv_t,:,
|
||||
|
||||
# Gettext.
|
||||
AM_GNU_GETTEXT([external], [need-formatstring-macros])
|
||||
AM_GNU_GETTEXT_VERSION([0.16])
|
||||
AM_GNU_GETTEXT_VERSION([0.21])
|
||||
|
||||
# Initialize the test suite.
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
@@ -366,8 +365,8 @@ AM_MISSING_PROG([AUTOM4TE], [autom4te])
|
||||
AC_SUBST(BACKUP_LIBEXEC_SCRIPTS)
|
||||
AC_SUBST(BACKUP_SBIN_SCRIPTS)
|
||||
AC_ARG_ENABLE(backup-scripts,
|
||||
AC_HELP_STRING([--enable-backup-scripts],
|
||||
[Create and install backup and restore scripts]),
|
||||
AS_HELP_STRING([--enable-backup-scripts],
|
||||
[Create and install backup and restore scripts]),
|
||||
[case $enableval in
|
||||
yes) BACKUP_LIBEXEC_SCRIPTS='$(BACKUP_LIBEXEC_SCRIPTS_LIST)'
|
||||
BACKUP_SBIN_SCRIPTS='$(BACKUP_SBIN_SCRIPTS_LIST)'
|
||||
@@ -381,7 +380,7 @@ else
|
||||
BACKUP_SED_COND='/^\#IF_DATE_FORMAT_OK/,/^\#ELSE_DATE_FORMAT_OK/d;/^\#ENDIF_DATE_FORMAT_OK/d'
|
||||
fi
|
||||
|
||||
AC_OUTPUT([Makefile\
|
||||
AC_CONFIG_FILES([Makefile\
|
||||
doc/Makefile\
|
||||
gnu/Makefile\
|
||||
lib/Makefile\
|
||||
@@ -389,3 +388,4 @@ AC_OUTPUT([Makefile\
|
||||
scripts/Makefile\
|
||||
rmt/Makefile\
|
||||
src/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%%comments:
|
||||
Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
Copyright 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar documentation.
|
||||
|
||||
# Copyright 1994-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -22,7 +22,6 @@ tar_TEXINFOS = \
|
||||
dumpdir.texi\
|
||||
tar-snapshot-edit.texi\
|
||||
fdl.texi\
|
||||
freemanuals.texi\
|
||||
genfile.texi\
|
||||
header.texi\
|
||||
intern.texi\
|
||||
@@ -139,9 +138,9 @@ check-docs:
|
||||
clean-local:
|
||||
rm -rf manual
|
||||
|
||||
GENDOCS=gendocs.sh
|
||||
GENDOCS=$(srcdir)/gendocs.sh
|
||||
|
||||
TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -E
|
||||
TEXI2DVI=texi2dvi -E
|
||||
|
||||
# Make sure you set TEXINPUTS
|
||||
# Usual value is:
|
||||
@@ -149,5 +148,8 @@ TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -E
|
||||
manual:
|
||||
TEXINPUTS=$(srcdir):$(top_srcdir)/build-tex:$(TEXINPUTS) \
|
||||
MAKEINFO="$(MAKEINFO) $(MAKEINFOFLAGS)" \
|
||||
TEXI2DVI="$(TEXI2DVI) -t @finalout" \
|
||||
TEXI2DVI="$(TEXI2DVI) -t '@set DISTRIB' -t @finalout" \
|
||||
$(GENDOCS) --texi2html tar 'GNU tar manual'
|
||||
|
||||
manual-rebuild: clean-local manual
|
||||
|
||||
|
||||
91
doc/README.manual
Normal file
91
doc/README.manual
Normal file
@@ -0,0 +1,91 @@
|
||||
* Overview
|
||||
|
||||
This file is a short instruction for maintainers on how to create and
|
||||
publish the online version of the Tar Manual.
|
||||
|
||||
In the sections below we assume that the tar project has been properly
|
||||
cloned from the git repo, bootstrapped and configured. We also assume
|
||||
that top-level directory of the project is the current local directory.
|
||||
|
||||
* Creating the web manual
|
||||
|
||||
To create the online version of the documentation, run
|
||||
|
||||
make -C doc manual-rebuild
|
||||
|
||||
This will create the directory doc/manual populated with the tar
|
||||
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.
|
||||
|
||||
For the completeness sake, there are two more Makefile goals related
|
||||
to the online manual:
|
||||
|
||||
** make -C doc clean-local
|
||||
|
||||
Removes the doc/manual directory, if it exists.
|
||||
|
||||
** make -C doc manual
|
||||
|
||||
Builds the doc/manual, unless it already exists.
|
||||
|
||||
* CVS Repository
|
||||
|
||||
The online tar manual[1] is a part of tar web pages[2] and is
|
||||
traditionally maintained in the CVS repository[3]. To publish the
|
||||
generated documentation, you will need first to check out tar web
|
||||
pages from the CVS. To do so, run
|
||||
|
||||
cvs -z3 -d:ext:<username>@cvs.savannah.gnu.org:/web/tar co tar
|
||||
|
||||
where <username> is your user name on Savannah. For the rest of this
|
||||
document we will assume that the checked out version of the tar web
|
||||
pages resides in the ~/websrc/tar directory.
|
||||
|
||||
If you have already checked out the web pages, be sure to update them
|
||||
before publishing:
|
||||
|
||||
cd ~/websrc/tar
|
||||
cvs update
|
||||
|
||||
* Publishing
|
||||
|
||||
To publish the created manual, change to the tar top-level directory
|
||||
and run:
|
||||
|
||||
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
|
||||
schedule any removed files for removal and any new files for addition
|
||||
to the repository:
|
||||
|
||||
cvs diff --brief 2>&1 | sed -n 's/.*cannot find //p' | xargs cvs rm
|
||||
cvs diff --brief 2>&1 | sed -n 's/^? //p' | xargs cvs add
|
||||
|
||||
Then commit your changes:
|
||||
|
||||
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
|
||||
the httpd daemon. Normally such synchronization happens within
|
||||
several seconds from the commit.
|
||||
|
||||
For more information about CVS, please see its documentation[4].
|
||||
|
||||
* References
|
||||
|
||||
[1] https://www.gnu.org/software/tar/manual/
|
||||
[2] https://www.gnu.org/software/tar/
|
||||
[3] https://web.cvs.savannah.gnu.org/viewvc/tar/
|
||||
[4] https://www.nongnu.org/cvs/#documentation
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: outline
|
||||
paragraph-separate: "[ ]*$"
|
||||
version-control: never
|
||||
End:
|
||||
@@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2025 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.
|
||||
|
||||
12
doc/fdl.texi
12
doc/fdl.texi
@@ -5,8 +5,9 @@
|
||||
@c hence no sectioning command or @node.
|
||||
|
||||
@display
|
||||
Copyright @copyright{} 2000-2019 Free Software Foundation, Inc.
|
||||
@uref{http://fsf.org/}
|
||||
Copyright @copyright{} 2000--2002, 2007--2008, 2022 Free Software
|
||||
Foundation, Inc.
|
||||
@uref{https://fsf.org/}
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
@@ -97,7 +98,7 @@ format, SGML or XML using a publicly available
|
||||
DTD, and standard-conforming simple HTML,
|
||||
PostScript or PDF designed for human modification. Examples
|
||||
of transparent image formats include PNG, XCF and
|
||||
JPG. Opaque formats include proprietary formats that can be
|
||||
JPG@. Opaque formats include proprietary formats that can be
|
||||
read and edited only by proprietary word processors, SGML or
|
||||
XML for which the DTD and/or processing tools are
|
||||
not generally available, and the machine-generated HTML,
|
||||
@@ -414,7 +415,7 @@ The Free Software Foundation may publish new, revised versions
|
||||
of the GNU Free Documentation License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns. See
|
||||
@uref{http://www.gnu.org/copyleft/}.
|
||||
@uref{https://www.gnu.org/licenses/}.
|
||||
|
||||
Each version of the License is given a distinguishing version number.
|
||||
If the Document specifies that a particular numbered version of this
|
||||
@@ -481,7 +482,7 @@ license notices just after the title page:
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
|
||||
replace the ``with@dots{}Texts.'' line with this:
|
||||
replace the ``with@dots{}Texts.''@: line with this:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
@@ -503,4 +504,3 @@ to permit their use in free software.
|
||||
@c Local Variables:
|
||||
@c ispell-local-pdict: "ispell-dict"
|
||||
@c End:
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
@cindex free documentation
|
||||
|
||||
The biggest deficiency in the free software community today is not in
|
||||
the software---it is the lack of good free documentation that we can
|
||||
include with the free software. Many of our most important
|
||||
programs do not come with free reference manuals and free introductory
|
||||
texts. Documentation is an essential part of any software package;
|
||||
when an important free software package does not come with a free
|
||||
manual and a free tutorial, that is a major gap. We have many such
|
||||
gaps today.
|
||||
|
||||
Consider Perl, for instance. The tutorial manuals that people
|
||||
normally use are non-free. How did this come about? Because the
|
||||
authors of those manuals published them with restrictive terms---no
|
||||
copying, no modification, source files not available---which exclude
|
||||
them from the free software world.
|
||||
|
||||
That wasn't the first time this sort of thing happened, and it was far
|
||||
from the last. Many times we have heard a GNU user eagerly describe a
|
||||
manual that he is writing, his intended contribution to the community,
|
||||
only to learn that he had ruined everything by signing a publication
|
||||
contract to make it non-free.
|
||||
|
||||
Free documentation, like free software, is a matter of freedom, not
|
||||
price. The problem with the non-free manual is not that publishers
|
||||
charge a price for printed copies---that in itself is fine. (The Free
|
||||
Software Foundation sells printed copies of manuals, too.) The
|
||||
problem is the restrictions on the use of the manual. Free manuals
|
||||
are available in source code form, and give you permission to copy and
|
||||
modify. Non-free manuals do not allow this.
|
||||
|
||||
The criteria of freedom for a free manual are roughly the same as for
|
||||
free software. Redistribution (including the normal kinds of
|
||||
commercial redistribution) must be permitted, so that the manual can
|
||||
accompany every copy of the program, both on-line and on paper.
|
||||
|
||||
Permission for modification of the technical content is crucial too.
|
||||
When people modify the software, adding or changing features, if they
|
||||
are conscientious they will change the manual too---so they can
|
||||
provide accurate and clear documentation for the modified program. A
|
||||
manual that leaves you no choice but to write a new manual to document
|
||||
a changed version of the program is not really available to our
|
||||
community.
|
||||
|
||||
Some kinds of limits on the way modification is handled are
|
||||
acceptable. For example, requirements to preserve the original
|
||||
author's copyright notice, the distribution terms, or the list of
|
||||
authors, are ok. It is also no problem to require modified versions
|
||||
to include notice that they were modified. Even entire sections that
|
||||
may not be deleted or changed are acceptable, as long as they deal
|
||||
with nontechnical topics (like this one). These kinds of restrictions
|
||||
are acceptable because they don't obstruct the community's normal use
|
||||
of the manual.
|
||||
|
||||
However, it must be possible to modify all the @emph{technical}
|
||||
content of the manual, and then distribute the result in all the usual
|
||||
media, through all the usual channels. Otherwise, the restrictions
|
||||
obstruct the use of the manual, it is not free, and we need another
|
||||
manual to replace it.
|
||||
|
||||
Please spread the word about this issue. Our community continues to
|
||||
lose manuals to proprietary publishing. If we spread the word that
|
||||
free software needs free reference manuals and free tutorials, perhaps
|
||||
the next person who wants to contribute by writing documentation will
|
||||
realize, before it is too late, that only free manuals contribute to
|
||||
the free software community.
|
||||
|
||||
If you are writing documentation, please insist on publishing it under
|
||||
the GNU Free Documentation License or another free documentation
|
||||
license. Remember that this decision requires your approval---you
|
||||
don't have to let the publisher decide. Some commercial publishers
|
||||
will use a free license if you insist, but they will not propose the
|
||||
option; it is up to you to raise the issue and say firmly that this is
|
||||
what you want. If the publisher you are dealing with refuses, please
|
||||
try other publishers. If you're not sure whether a proposed license
|
||||
is free, write to @email{licensing@@gnu.org}.
|
||||
|
||||
You can encourage commercial publishers to sell more free, copylefted
|
||||
manuals and tutorials by buying them, and particularly by buying
|
||||
copies from the publishers that paid for their writing or for major
|
||||
improvements. Meanwhile, try to avoid buying non-free documentation
|
||||
at all. Check the distribution terms of a manual before you buy it,
|
||||
and insist that whoever seeks your business must respect your freedom.
|
||||
Check the history of the book, and try reward the publishers that have
|
||||
paid or pay the authors to work on it.
|
||||
|
||||
The Free Software Foundation maintains a list of free documentation
|
||||
published by other publishers, at
|
||||
@url{http://www.fsf.org/doc/other-free-books.html}.
|
||||
512
doc/gendocs.sh
Executable file
512
doc/gendocs.sh
Executable file
@@ -0,0 +1,512 @@
|
||||
#!/bin/sh -e
|
||||
# gendocs.sh -- generate a GNU manual in many formats. This script is
|
||||
# mentioned in maintain.texi. See the help message below for usage details.
|
||||
|
||||
scriptversion=2021-03-01.13
|
||||
|
||||
# Copyright 2003-2025 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, 2025 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:
|
||||
174
doc/gendocs_template
Executable file → Normal file
174
doc/gendocs_template
Executable file → Normal file
@@ -1,125 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!-- $Id: gendocs_template,v 1.5 2007/10/30 14:58:52 gray Exp $ -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<!--#include virtual="/server/header.html" -->
|
||||
<!-- Parent-Version: 1.78 -->
|
||||
|
||||
<head>
|
||||
<title>%%TITLE%% - GNU Project - Free Software Foundation (FSF)</title>
|
||||
<meta http-equiv="content-type" content='text/html; charset=utf-8' />
|
||||
<link rel="stylesheet" type="text/css" href="/gnu.css" />
|
||||
<link rev="made" href="mailto:gray@gnu.org" />
|
||||
<link rel="icon" type="image/png" href="/graphics/gnu-head-icon.png" />
|
||||
</head>
|
||||
<!--
|
||||
Copyright (C) 2006-2025 Free Software Foundation, Inc.
|
||||
|
||||
<!-- This document is in XML, and xhtml 1.0 -->
|
||||
<!-- Please make sure to properly nest your tags -->
|
||||
<!-- and ensure that your final document validates -->
|
||||
<!-- consistent with W3C xhtml 1.0 and CSS standards -->
|
||||
<!-- See validator.w3.org -->
|
||||
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.
|
||||
-->
|
||||
|
||||
<body>
|
||||
|
||||
<h3>%%TITLE%%</h3>
|
||||
<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>
|
||||
<a href="/graphics/gnu-head.jpg">
|
||||
<img src="/graphics/gnu-head-sm.jpg"
|
||||
alt=" [image of the head of a GNU] "
|
||||
width="129" height="122" />
|
||||
</a>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<p>The manual for %%PACKAGE%% is available in the following formats:</p>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 characters gzipped tar file)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.txt">ASCII text
|
||||
(%%ASCII_SIZE%%K characters)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.txt.gz">ASCII text compressed
|
||||
(%%ASCII_GZ_SIZE%%K gzipped characters)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.dvi.gz">TeX dvi file
|
||||
(%%DVI_GZ_SIZE%%K characters gzipped)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.ps.gz">PostScript file
|
||||
(%%PS_GZ_SIZE%%K characters gzipped)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.pdf">PDF file
|
||||
(%%PDF_SIZE%%K characters)</a>.</li>
|
||||
<li><a href="%%PACKAGE%%.texi.tar.gz">Texinfo source
|
||||
(%%TEXI_TGZ_SIZE%%K characters gzipped tar file)</a></li>
|
||||
<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>(This page generated by the <a
|
||||
href="%%SCRIPTURL%%">%%SCRIPTNAME%%</a> script.)
|
||||
</p>
|
||||
<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>
|
||||
<a href="http://validator.w3.org/check?uri=referer"><img
|
||||
src="http://www.w3.org/Icons/valid-xhtml10"
|
||||
alt="Valid XHTML 1.0!" height="31" width="88" /></a>
|
||||
</p>
|
||||
<p>(This page generated by the <a href="%%SCRIPTURL%%">%%SCRIPTNAME%%
|
||||
script</a>.)</p>
|
||||
|
||||
<div class="copyright">
|
||||
<p>
|
||||
Return to the <a href="/home.html">GNU Project home page</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 FSF & GNU inquiries to
|
||||
<a href="mailto:gnu@gnu.org"><em>gnu@gnu.org</em></a>.
|
||||
There are also <a href="/home.html#ContactInfo">other ways to contact</a>
|
||||
the FSF.
|
||||
<br />
|
||||
Please send broken links and other corrections (or suggestions) to
|
||||
<a href="mailto:webmasters@gnu.org"><em>webmasters@gnu.org</em></a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Copyright 2004-2019 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02111, USA
|
||||
<br />
|
||||
Verbatim copying and distribution of this entire article is
|
||||
permitted in any medium, provided this notice is preserved.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Updated:
|
||||
<!-- timestamp start -->
|
||||
$Date: 2007/10/30 14:58:52 $ $Author: gray $
|
||||
<!-- timestamp end -->
|
||||
</p>
|
||||
<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, 2025 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-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2025 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.1 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
@node Standard
|
||||
@unnumberedsec Basic Tar Format
|
||||
@UNREVISED
|
||||
@UNREVISED{}
|
||||
|
||||
While an archive may contain many files, the archive itself is a
|
||||
single ordinary file. Like any other file, an archive file can be
|
||||
@@ -51,7 +51,10 @@ of the file. At the end of the archive file there are two 512-byte blocks
|
||||
filled with binary zeros as an end-of-file marker. A reasonable system
|
||||
should write such end-of-file marker at the end of an archive, but
|
||||
must not assume that such a block exists when reading an archive. In
|
||||
particular @GNUTAR{} always issues a warning if it does not encounter it.
|
||||
particular, @GNUTAR{} does not treat missing end-of-file marker as an
|
||||
error and silently ignores the fact. You can instruct it to issue
|
||||
a warning, however, by using the @option{--warning=missing-zero-blocks}
|
||||
option (@pxref{General Warnings, missing-zero-blocks}).
|
||||
|
||||
The blocks may be @dfn{blocked} for physical I/O operations.
|
||||
Each record of @var{n} blocks (where @var{n} is set by the
|
||||
@@ -111,8 +114,9 @@ The @code{uid} and @code{gid} fields are the numeric user and group
|
||||
not support numeric user or group @acronym{ID}s, these fields should
|
||||
be ignored.
|
||||
|
||||
The @code{size} field is the size of the file in bytes; linked files
|
||||
are archived with this field specified as zero.
|
||||
The @code{size} field is the size of the file in bytes; for archive
|
||||
members that are symbolic or hard links to another file, this field
|
||||
is specified as zero.
|
||||
|
||||
The @code{mtime} field represents the data modification time of the file at
|
||||
the time it was archived. It represents the integer number of
|
||||
@@ -123,7 +127,7 @@ the simple sum of all bytes in the header block. Each 8-bit
|
||||
byte in the header is added to an unsigned integer, initialized to
|
||||
zero, the precision of which shall be no less than seventeen bits.
|
||||
When calculating the checksum, the @code{chksum} field is treated as
|
||||
if it were all blanks.
|
||||
if it were filled with spaces (ASCII 32).
|
||||
|
||||
The @code{typeflag} field specifies the type of file archived. If a
|
||||
particular implementation does not recognize or permit the specified
|
||||
@@ -266,7 +270,7 @@ IEEE Std 1003.2-1992, pages 380-388 (section 4.48) and pages 936-940
|
||||
|
||||
@node Extensions
|
||||
@unnumberedsec @acronym{GNU} Extensions to the Archive Format
|
||||
@UNREVISED
|
||||
@UNREVISED{}
|
||||
|
||||
The @acronym{GNU} format uses additional file types to describe new types of
|
||||
files in an archive. These are listed below.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; mastermenu.el --- Redefinition of texinfo-master-menu-list
|
||||
|
||||
;; Copyright 2006-2019 Free Software Foundation, Inc.
|
||||
;; Copyright 2006-2025 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-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2017--2025 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.3 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@c This is part of GNU tar manual.
|
||||
@c Copyright 1992-2019 Free Software Foundation, Inc.
|
||||
@c Copyright 1992--2025 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-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2005--2025 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@c This is part of the paxutils manual.
|
||||
@c Copyright (C) 2006-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2006--2025 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-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2007--2025 Free Software Foundation, Inc.
|
||||
@c This file is distributed under GFDL 1.1 or any later version
|
||||
@c published by the Free Software Foundation.
|
||||
|
||||
|
||||
181
doc/tar.1
181
doc/tar.1
@@ -1,5 +1,4 @@
|
||||
.\" This file is part of GNU tar. -*- nroff -*-
|
||||
.\" Copyright 2013-2019 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 "February 4, 2019" "TAR" "GNU TAR Manual"
|
||||
.TH TAR 1 "January 1, 2025" "TAR" "GNU TAR Manual"
|
||||
.SH NAME
|
||||
tar \- an archiving utility
|
||||
.SH SYNOPSIS
|
||||
@@ -21,23 +20,21 @@ 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] \fIARCHIVE\fR \fIARCHIVE\fR
|
||||
\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...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-d\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-t\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-r\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-t\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-u\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.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] \fIARCHIVE\fR \fIARCHIVE\fR
|
||||
\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...]
|
||||
.sp
|
||||
@@ -45,17 +42,15 @@ tar \- an archiving utility
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-delete\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-append\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
\fBtar\fR \fB\-\-append\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-list\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
\fBtar\fR \fB\-\-list\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-test\-label\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fILABEL\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-update\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.sp
|
||||
\fBtar\fR \fB\-\-update\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
|
||||
.sp
|
||||
\fBtar\fR {\fB\-\-extract\fR|\fB\-\-get\fR} [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
\fBtar\fR {\fB\-\-extract\fR|\fB\-\-get\fR} [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
|
||||
.SH NOTE
|
||||
This manpage is a short description of GNU \fBtar\fR. For a detailed
|
||||
discussion, including examples and usage recommendations, refer to the
|
||||
@@ -74,7 +69,7 @@ You can also view the manual using the info mode in
|
||||
or find it in various formats online at
|
||||
.PP
|
||||
.RS +4
|
||||
.B http://www.gnu.org/software/tar/manual
|
||||
.B https://www.gnu.org/software/tar/manual
|
||||
.RE
|
||||
.PP
|
||||
If any discrepancies occur between this manpage and the
|
||||
@@ -85,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
|
||||
@@ -97,8 +90,8 @@ In
|
||||
the first argument is a cluster of option letters and all subsequent
|
||||
arguments supply arguments to those options that require them. The
|
||||
arguments are read in the same order as the option letters. Any
|
||||
command line words that remain after all options has been processed
|
||||
are treated as non-optional arguments: file or archive member names.
|
||||
command line words that remain after all options have been processed
|
||||
are treated as non-option arguments: file or archive member names.
|
||||
.PP
|
||||
For example, the \fBc\fR option requires creating the archive, the
|
||||
\fBv\fR option requests the verbose operation, and the \fBf\fR option
|
||||
@@ -107,35 +100,37 @@ The following command, written in the traditional style, instructs tar
|
||||
to store all files from the directory
|
||||
.B /etc
|
||||
into the archive file
|
||||
.B etc.tar
|
||||
.BR etc.tar ,
|
||||
verbosely listing the files being archived:
|
||||
.PP
|
||||
.EX
|
||||
.B tar cfv etc.tar /etc
|
||||
tar cfv etc.tar /etc
|
||||
.EE
|
||||
.PP
|
||||
In
|
||||
.BR "UNIX " or " short-option style" ,
|
||||
each option letter is prefixed with a single dash, as in other command
|
||||
line utilities. If an option takes argument, the argument follows it,
|
||||
line utilities. If an option takes an argument, the argument follows it,
|
||||
either as a separate command line word, or immediately following the
|
||||
option. However, if the option takes an \fBoptional\fR argument, the
|
||||
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. Options
|
||||
that take arguments (whether mandatory or optional), can appear at
|
||||
the end of such a cluster, e.g. \fB\-vkpf a.tar\fR.
|
||||
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.
|
||||
.PP
|
||||
The example command above written in the
|
||||
.B short-option style
|
||||
could look like:
|
||||
.PP
|
||||
.EX
|
||||
.B tar -cvf etc.tar /etc
|
||||
tar \-cvf etc.tar /etc
|
||||
.EE
|
||||
or
|
||||
.B tar -c -v -f etc.tar /etc
|
||||
.EX
|
||||
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
|
||||
.B tar --create --file etc.tar --verbose /etc
|
||||
tar \-\-create \-\-file etc.tar \-\-verbose /etc
|
||||
.EE
|
||||
or (abbreviating some options):
|
||||
.EX
|
||||
.B 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
|
||||
@@ -164,17 +159,17 @@ with old options is not encouraged.
|
||||
.SS Operation mode
|
||||
The options listed in the table below tell GNU \fBtar\fR what
|
||||
operation it is to perform. Exactly one of them must be given.
|
||||
Meaning of non-optional arguments depends on the operation mode
|
||||
The meaning of non-option arguments depends on the operation mode
|
||||
requested.
|
||||
.TP
|
||||
\fB\-A\fR, \fB\-\-catenate\fR, \fB\-\-concatenate\fR
|
||||
Append archive to the end of another archive. The arguments are
|
||||
Append archives to the end of another archive. The arguments are
|
||||
treated as the names of archives to append. All archives must be of
|
||||
the same format as the archive they are appended to, otherwise the
|
||||
resulting archive might be unusable with non-GNU implementations of
|
||||
\fBtar\fR. Notice also that when more than one archive is given, the
|
||||
members from archives other than the first one will be accessible in
|
||||
the resulting archive only if using the \fB\-i\fR
|
||||
the resulting archive only when using the \fB\-i\fR
|
||||
(\fB\-\-ignore\-zeros\fR) option.
|
||||
|
||||
Compressed archives cannot be concatenated.
|
||||
@@ -218,7 +213,7 @@ There is no short option equivalent for this option.
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-update\fR
|
||||
Append files which are newer than the corresponding copy in the
|
||||
archive. Arguments have the same meaning as with \fB\-c\fR and
|
||||
archive. Arguments have the same meaning as with the \fB\-c\fR and
|
||||
\fB\-r\fR options. Notice, that newer files don't replace their
|
||||
old archive copies, but instead are appended to the end of archive.
|
||||
The resulting archive can thus contain several members of the
|
||||
@@ -228,16 +223,14 @@ 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. No
|
||||
arguments are allowed.
|
||||
Show built-in defaults for various \fBtar\fR options and exit.
|
||||
.TP
|
||||
\fB\-?\fR, \fB\-\-help
|
||||
Display a short option summary and exit. No arguments allowed.
|
||||
Display a short option summary and exit.
|
||||
.TP
|
||||
\fB\-\-usage\fR
|
||||
Display a list of available options and exit. No arguments allowed.
|
||||
Display a list of available options and exit.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Print program version and copyright information and exit.
|
||||
@@ -249,16 +242,15 @@ Check device numbers when creating incremental archives (default).
|
||||
.TP
|
||||
\fB\-g\fR, \fB\-\-listed\-incremental\fR=\fIFILE\fR
|
||||
Handle new GNU-format incremental backups. \fIFILE\fR is the name of
|
||||
a \fBsnapshot file\fR, where tar stores additional information which
|
||||
a \fBsnapshot file\fR, where \fBtar\fR stores additional information which
|
||||
is used to decide which files changed since the previous incremental
|
||||
dump and, consequently, must be dumped again. If \fIFILE\fR does not
|
||||
exist when creating an archive, it will be created and all files will
|
||||
be added to the resulting archive (the \fBlevel 0\fR dump). To create
|
||||
incremental archives of non-zero level \fBN\fR, create a copy of the
|
||||
snapshot file created during the level \fBN-1\fR, and use it as
|
||||
\fIFILE\fR.
|
||||
incremental archives of non-zero level \fBN\fR, you need a copy of the
|
||||
snapshot file created for level \fBN-1\fR, and use it as \fIFILE\fR.
|
||||
|
||||
When listing or extracting, the actual contents of \fIFILE\fR is not
|
||||
When listing or extracting, the actual content of \fIFILE\fR is not
|
||||
inspected, it is needed only due to syntactical requirements. It is
|
||||
therefore common practice to use \fB/dev/null\fR in its place.
|
||||
.TP
|
||||
@@ -275,7 +267,7 @@ Handle old GNU-format incremental backups.
|
||||
Do not exit with nonzero on unreadable files.
|
||||
.TP
|
||||
\fB\-\-level\fR=\fINUMBER\fR
|
||||
Set dump level for created listed-incremental archive. Currently only
|
||||
Set dump level for a created listed-incremental archive. Currently only
|
||||
\fB\-\-level=0\fR is meaningful: it instructs \fBtar\fR to truncate
|
||||
the snapshot file before dumping, thereby forcing a level 0 dump.
|
||||
.TP
|
||||
@@ -283,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
|
||||
@@ -307,7 +299,7 @@ either on the command line or via the \fB\-T\fR option. The default
|
||||
Disable the use of some potentially harmful options.
|
||||
.TP
|
||||
\fB\-\-sparse\-version\fR=\fIMAJOR\fR[.\fIMINOR\fR]
|
||||
Set version of the sparse format to use (implies \fB\-\-sparse\fR).
|
||||
Set which version of the sparse format to use.
|
||||
This option implies
|
||||
.BR \-\-sparse .
|
||||
Valid argument values are
|
||||
@@ -315,7 +307,7 @@ Valid argument values are
|
||||
.BR 0.1 ", and"
|
||||
.BR 1.0 .
|
||||
For a detailed discussion of sparse formats, refer to the \fBGNU Tar
|
||||
Manual\fR, appendix \fBD\fR, "\fBSparse Formats\fR". Using \fBinfo\fR
|
||||
Manual\fR, appendix \fBD\fR, "\fBSparse Formats\fR". Using the \fBinfo\fR
|
||||
reader, it can be accessed running the following command:
|
||||
.BR "info tar 'Sparse Formats'" .
|
||||
.TP
|
||||
@@ -370,7 +362,6 @@ Verify the archive after writing it.
|
||||
.SS Output stream selection
|
||||
.TP
|
||||
\fB\-\-ignore\-command\-error\fR
|
||||
.TP
|
||||
Ignore subprocess exit codes.
|
||||
.TP
|
||||
\fB\-\-no\-ignore\-command\-error\fR
|
||||
@@ -450,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
|
||||
@@ -463,6 +454,7 @@ Format of the archive being processed. One of:
|
||||
.BR posix ,
|
||||
.BR ustar ,
|
||||
.BR v7 .
|
||||
.TP
|
||||
.B TAR_SUBCOMMAND
|
||||
A short option (with a leading dash) describing the operation \fBtar\fR is
|
||||
executing.
|
||||
@@ -472,7 +464,7 @@ executing.
|
||||
\fB\-\-atime\-preserve\fR[=\fIMETHOD\fR]
|
||||
Preserve access times on dumped files, either by restoring the times
|
||||
after reading (\fIMETHOD\fR=\fBreplace\fR, this is the default) or by
|
||||
not setting the times in the first place (\fIMETHOD\fR=\fBsystem\fR)
|
||||
not setting the times in the first place (\fIMETHOD\fR=\fBsystem\fR).
|
||||
.TP
|
||||
\fB\-\-delay\-directory\-restore\fR
|
||||
Delay setting modification times and permissions of extracted
|
||||
@@ -560,14 +552,16 @@ As a result, each input file owned by \fIOLDUSR\fR will be
|
||||
stored in archive with owner name \fINEWUSR\fR and UID \fINEWUID\fR.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-preserve\-permissions\fR, \fB\-\-same\-permissions\fR
|
||||
extract information about file permissions (default for superuser)
|
||||
Set permissions of extracted files to those recorded in the archive
|
||||
(default for superuser).
|
||||
.TP
|
||||
\fB\-\-same\-owner\fR
|
||||
Try extracting files with the same ownership as exists in the archive
|
||||
(default for superuser).
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-preserve\-order\fR, \fB\-\-same\-order\fR
|
||||
Sort names to extract to match archive
|
||||
Tell \fBtar\fR that the list of file names to process is sorted in the
|
||||
same order as the files in the archive.
|
||||
.TP
|
||||
\fB\-\-sort=\fIORDER\fR
|
||||
When creating an archive, sort directory entries according to
|
||||
@@ -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
|
||||
@@ -607,13 +601,13 @@ Enable extended attributes support.
|
||||
Disable extended attributes support.
|
||||
.TP
|
||||
.BI \-\-xattrs\-exclude= PATTERN
|
||||
Specify the exclude pattern for xattr keys. \fIPATTERN\fR is a POSIX
|
||||
regular expression, e.g. \fB\-\-xattrs\-exclude='^user\.'\fR, to exclude
|
||||
Specify the exclude pattern for xattr keys. \fIPATTERN\fR is a globbing
|
||||
pattern, e.g.\& \fB\-\-xattrs\-exclude='user.*'\fR to include only
|
||||
attributes from the user namespace.
|
||||
.TP
|
||||
.BI \-\-xattrs\-include= PATTERN
|
||||
Specify the include pattern for xattr keys. \fIPATTERN\fR is a POSIX
|
||||
regular expression.
|
||||
Specify the include pattern for xattr keys. \fIPATTERN\fR is a globbing
|
||||
pattern.
|
||||
.SS Device selection and switching
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-file\fR=\fIARCHIVE\fR
|
||||
@@ -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
|
||||
@@ -741,14 +735,14 @@ end-of-file marker.
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-ignore\-zeros\fR
|
||||
Ignore zeroed blocks in archive. Normally two consecutive 512-blocks
|
||||
filled with zeroes mean EOF and tar stops reading after encountering
|
||||
filled with zeroes mean EOF and \fBtar\fR stops reading after encountering
|
||||
them. This option instructs it to read further and is useful when
|
||||
reading archives created with the \fB\-A\fR option.
|
||||
.TP
|
||||
\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.
|
||||
@@ -780,7 +774,8 @@ Same as \fB\-\-format=v7\fR.
|
||||
\fB\-\-pax\-option\fR=\fIkeyword\fR[[:]=\fIvalue\fR][,\fIkeyword\fR[[:]=\fIvalue\fR]]...
|
||||
Control pax keywords when creating \fBPAX\fR archives (\fB\-H
|
||||
pax\fR). This option is equivalent to the \fB\-o\fR option of the
|
||||
.BR pax (1) utility.
|
||||
.BR pax (1)
|
||||
utility.
|
||||
.TP
|
||||
\fB\-\-posix\fR
|
||||
Same as \fB\-\-format=posix\fR.
|
||||
@@ -853,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
|
||||
@@ -875,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.
|
||||
@@ -960,7 +961,7 @@ See also \fB\-\-verbatim\-files\-from\fR.
|
||||
.TP
|
||||
\fB\-N\fR, \fB\-\-newer\fR=\fIDATE\fR, \fB\-\-after\-date\fR=\fIDATE\fR
|
||||
Only store files newer than DATE. If \fIDATE\fR starts with \fB/\fR
|
||||
or \fB.\fR it is taken to be a file name; the ctime of that file is
|
||||
or \fB.\fR it is taken to be a file name; the mtime of that file is
|
||||
used as the date.
|
||||
.TP
|
||||
\fB\-\-one\-file\-system\fR
|
||||
@@ -980,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.
|
||||
@@ -1003,13 +1004,13 @@ 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.
|
||||
|
||||
This option affects all \fB\-\-files\-from\fR options that occur after
|
||||
it in the command line. Its effect is reverted by the
|
||||
\fB\-\-no\-verbatim\-files\-from} option.
|
||||
\fB\-\-no\-verbatim\-files\-from\fR option.
|
||||
|
||||
This option is implied by the \fB\-\-null\fR option.
|
||||
|
||||
@@ -1059,7 +1060,8 @@ Display progress messages every \fIN\fRth record (default 10).
|
||||
Run \fIACTION\fR on each checkpoint.
|
||||
.TP
|
||||
\fB\-\-clamp\-mtime\fR
|
||||
Only set time when the file is more recent than what was given with \-\-mtime.
|
||||
Only set time when the file is more recent than what was given with
|
||||
\fB\-\-mtime\fR.
|
||||
.TP
|
||||
\fB\-\-full\-time\fR
|
||||
Print file time to its full resolution.
|
||||
@@ -1118,14 +1120,14 @@ Verbosely list files processed. Each instance of this option on the
|
||||
command line increases the verbosity level by one. The maximum
|
||||
verbosity level is 3. For a detailed discussion of how various
|
||||
verbosity levels affect tar's output, please refer to \fBGNU Tar
|
||||
Manual\fR, subsection 2.5.1 "\fBThe \-\-verbose Option\fR".
|
||||
Manual\fR, subsection 2.5.2 "\fBThe '\-\-verbose' Option\fR".
|
||||
.TP
|
||||
\fB\-\-warning\fR=\fIKEYWORD\fR
|
||||
Enable or disable warning messages identified by \fIKEYWORD\fR. The
|
||||
messages are suppressed if \fIKEYWORD\fR is prefixed with \fBno\-\fR
|
||||
and enabled otherwise.
|
||||
|
||||
Multiple \fB\-\-warning\fR messages accumulate.
|
||||
Multiple \fB\-\-warning\fR options accumulate.
|
||||
|
||||
Keywords controlling general \fBtar\fR operation:
|
||||
.RS
|
||||
@@ -1142,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"
|
||||
@@ -1164,7 +1166,7 @@ Keywords applicable for \fBtar --create\fR:
|
||||
"%s: file is unchanged; not dumped"
|
||||
.TP
|
||||
.B ignore-archive
|
||||
"%s: file is the archive; not dumped"
|
||||
"%s: archive cannot contain itself; not dumped"
|
||||
.TP
|
||||
.B file-removed
|
||||
"%s: File removed before we read it"
|
||||
@@ -1178,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"
|
||||
@@ -1210,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
|
||||
@@ -1247,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
|
||||
@@ -1263,9 +1264,8 @@ 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 exit code indicates whether it was able to successfully perform
|
||||
Tar's exit code indicates whether it was able to successfully perform
|
||||
the requested operation, and if not, what kind of error occurred.
|
||||
.TP
|
||||
.B 0
|
||||
@@ -1273,12 +1273,13 @@ Successful termination.
|
||||
.TP
|
||||
.B 1
|
||||
.I Some files differ.
|
||||
If tar was invoked with the \fB\-\-compare\fR (\fB\-\-diff\fR, \fB\-d\fR)
|
||||
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 tar 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.
|
||||
@@ -1289,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.
|
||||
@@ -1314,11 +1315,11 @@ Online copies of \fBGNU tar\fR documentation in various formats can be
|
||||
found at:
|
||||
.PP
|
||||
.in +4
|
||||
.B http://www.gnu.org/software/tar/manual
|
||||
.B https://www.gnu.org/software/tar/manual
|
||||
.SH "BUG REPORTS"
|
||||
Report bugs to <bug\-tar@gnu.org>.
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2013 Free Software Foundation, Inc.
|
||||
Copyright \(co 2013\(en2025 Free Software Foundation, Inc.
|
||||
.br
|
||||
.na
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
|
||||
937
doc/tar.texi
937
doc/tar.texi
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Copyright 2006-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2025 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-2019 Free Software Foundation, Inc.
|
||||
@c Copyright 1992--2025 Free Software Foundation, Inc.
|
||||
@c See file tar.texi for copying conditions.
|
||||
|
||||
@macro GNUTAR
|
||||
|
||||
2
gnulib
2
gnulib
Submodule gnulib updated: 4652c7bafa...4619f63e6f
@@ -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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2005-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -18,15 +18,23 @@
|
||||
# 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
|
||||
@@ -39,8 +47,10 @@ fdopendir
|
||||
fdutimensat
|
||||
file-has-acl
|
||||
fileblocks
|
||||
flexmember
|
||||
fnmatch-gnu
|
||||
fprintftime
|
||||
free-posix
|
||||
fseeko
|
||||
fstatat
|
||||
full-write
|
||||
@@ -48,17 +58,24 @@ futimens
|
||||
getline
|
||||
getopt-gnu
|
||||
getpagesize
|
||||
gettext
|
||||
gettext-h
|
||||
gettime
|
||||
gitlog-to-changelog
|
||||
hash
|
||||
human
|
||||
ialloc
|
||||
idx
|
||||
intprops
|
||||
inttostr
|
||||
inttypes
|
||||
inttypes-h
|
||||
largefile
|
||||
lchown
|
||||
limits-h
|
||||
linkat
|
||||
localcharset
|
||||
manywarnings
|
||||
mbrtoc32-regular
|
||||
mcel-prefer
|
||||
mkdirat
|
||||
mkdtemp
|
||||
mkfifoat
|
||||
@@ -71,26 +88,27 @@ progname
|
||||
quote
|
||||
quotearg
|
||||
readlinkat
|
||||
reallocarray
|
||||
renameat
|
||||
root-uid
|
||||
rpmatch
|
||||
full-read
|
||||
safe-read
|
||||
same-inode
|
||||
savedir
|
||||
selinux-at
|
||||
setenv
|
||||
snprintf
|
||||
stat-time
|
||||
stdbool
|
||||
stdint
|
||||
std-gnu23
|
||||
stddef-h
|
||||
stdint-h
|
||||
stpcpy
|
||||
stdopen
|
||||
strdup-posix
|
||||
strerror
|
||||
strnlen
|
||||
strtoimax
|
||||
strtol
|
||||
strtoul
|
||||
strtoumax
|
||||
symlinkat
|
||||
sys_stat-h
|
||||
timespec
|
||||
timespec-sub
|
||||
unlinkat
|
||||
@@ -98,8 +116,11 @@ unlinkdir
|
||||
unlocked-io
|
||||
utimensat
|
||||
version-etc-fsf
|
||||
verror
|
||||
xalignalloc
|
||||
xalloc
|
||||
xalloc-die
|
||||
xgetcwd
|
||||
xstrtoumax
|
||||
xvasprintf
|
||||
year2038-recommended
|
||||
|
||||
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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -32,9 +32,7 @@ AM_CFLAGS = $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
|
||||
noinst_HEADERS = \
|
||||
paxlib.h\
|
||||
rmt.h\
|
||||
stdopen.h\
|
||||
system.h\
|
||||
system-ioctl.h\
|
||||
wordsplit.h\
|
||||
xattr-at.h
|
||||
|
||||
@@ -42,8 +40,7 @@ libtar_a_SOURCES = \
|
||||
paxerror.c paxexit-status.c paxlib.h paxnames.c \
|
||||
rtapelib.c \
|
||||
rmt.h \
|
||||
stdopen.c stdopen.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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2012-2025 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
|
||||
@@ -22,39 +22,39 @@
|
||||
#endif
|
||||
|
||||
/* setting */
|
||||
static inline int setxattr (const char *path, const char *name, const void
|
||||
*value, size_t size, int flags)
|
||||
static int setxattr (const char *path, const char *name, const void
|
||||
*value, size_t size, int flags)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
static inline int lsetxattr (const char *path, const char *name, const void
|
||||
*value, size_t size, int flags)
|
||||
static int lsetxattr (const char *path, const char *name, const void
|
||||
*value, size_t size, int flags)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
static inline int fsetxattr (int filedes, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
static int fsetxattr (int filedes, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
|
||||
/* getting */
|
||||
static inline ssize_t getxattr (const char *path, const char *name, void *value,
|
||||
size_t size)
|
||||
static ssize_t getxattr (const char *path, const char *name, void *value,
|
||||
size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
static inline ssize_t lgetxattr (const char *path, const char *name, void
|
||||
*value, size_t size)
|
||||
static ssize_t lgetxattr (const char *path, const char *name, void
|
||||
*value, size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
static inline ssize_t fgetxattr (int filedes, const char *name, void *value,
|
||||
size_t size)
|
||||
static ssize_t fgetxattr (int filedes, const char *name, void *value,
|
||||
size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
|
||||
/* listing */
|
||||
static inline ssize_t listxattr (const char *path, char *list, size_t size)
|
||||
static ssize_t listxattr (const char *path, char *list, size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
static inline ssize_t llistxattr (const char *path, char *list, size_t size)
|
||||
static ssize_t llistxattr (const char *path, char *list, size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
static inline ssize_t flistxattr (int filedes, char *list, size_t size)
|
||||
static ssize_t flistxattr (int filedes, char *list, size_t size)
|
||||
{ errno = ENOTSUP; return -1; }
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/* stdopen.c - ensure that the three standard file descriptors are in use
|
||||
|
||||
Copyright 2005-2019 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, 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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert and Jim Meyering. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "stdopen.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Try to ensure that all of the standard file numbers (0, 1, 2)
|
||||
are in use. Without this, each application would have to guard
|
||||
every call to open, dup, fopen, etc. with tests to ensure they
|
||||
don't use one of the special file numbers when opening a file.
|
||||
Return false if at least one of the file descriptors is initially
|
||||
closed and an attempt to reopen it fails. Otherwise, return true. */
|
||||
bool
|
||||
stdopen (void)
|
||||
{
|
||||
int fd;
|
||||
bool ok = true;
|
||||
|
||||
for (fd = 0; fd <= 2; fd++)
|
||||
{
|
||||
if (fcntl (fd, F_GETFD) < 0)
|
||||
{
|
||||
if (errno != EBADF)
|
||||
ok = false;
|
||||
else
|
||||
{
|
||||
static const int contrary_mode[]
|
||||
= { O_WRONLY, O_RDONLY, O_RDONLY };
|
||||
int mode = contrary_mode[fd];
|
||||
int new_fd;
|
||||
/* Open /dev/null with the contrary mode so that the typical
|
||||
read (stdin) or write (stdout, stderr) operation will fail.
|
||||
With descriptor 0, we can do even better on systems that
|
||||
have /dev/full, by opening that write-only instead of
|
||||
/dev/null. The only drawback is that a write-provoked
|
||||
failure comes with a misleading errno value, ENOSPC. */
|
||||
if (mode == O_RDONLY
|
||||
|| (new_fd = open ("/dev/full", mode) != fd))
|
||||
new_fd = open ("/dev/null", mode);
|
||||
if (new_fd != fd)
|
||||
{
|
||||
if (0 <= new_fd)
|
||||
close (new_fd);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef STDOPEN_H
|
||||
# define STDOPEN_H 1
|
||||
|
||||
# include <stdbool.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
bool stdopen (void);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
||||
982
lib/wordsplit.c
982
lib/wordsplit.c
File diff suppressed because it is too large
Load Diff
@@ -18,14 +18,15 @@
|
||||
#define __WORDSPLIT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <idx.h>
|
||||
|
||||
#if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
|
||||
# define __WORDSPLIT_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
|
||||
#else
|
||||
# define __WORDSPLIT_ATTRIBUTE_FORMAT(spec) /* empty */
|
||||
#endif
|
||||
/* 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,47 +37,47 @@ typedef struct wordsplit wordsplit_t;
|
||||
must be set (or unset, if starting with !) in ws_flags (if starting with
|
||||
WRDSF_) or ws_options (if starting with WRDSO_) to initialize or use the
|
||||
given member.
|
||||
|
||||
|
||||
If not redefined explicitly, most of them are set to some reasonable
|
||||
default value upon entry to wordsplit(). */
|
||||
struct 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 accomodate. */
|
||||
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;
|
||||
|
||||
int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
|
||||
idx_t ws_envidx;
|
||||
idx_t ws_envsiz;
|
||||
|
||||
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
|
||||
@@ -90,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
|
||||
@@ -100,23 +101,21 @@ struct wordsplit
|
||||
|
||||
See ws_getvar for a discussion of possible
|
||||
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
|
||||
|
||||
const char *ws_input; /* Input string (the S argument to wordsplit. */
|
||||
idx_t ws_len; /* Length of ws_input. */
|
||||
idx_t ws_endp; /* Points past the last processed byte in
|
||||
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. */
|
||||
@@ -221,19 +220,15 @@ struct wordsplit
|
||||
/* Handle hex escapes in quoted strings */
|
||||
#define WRDSO_XESC_QUOTE 0x00000400
|
||||
|
||||
#define WRDSO_BSKEEP WRDSO_BSKEEP_WORD
|
||||
#define WRDSO_OESC WRDSO_OESC_WORD
|
||||
#define WRDSO_XESC WRDSO_XESC_WORD
|
||||
#define WRDSO_BSKEEP WRDSO_BSKEEP_WORD
|
||||
#define WRDSO_OESC WRDSO_OESC_WORD
|
||||
#define WRDSO_XESC WRDSO_XESC_WORD
|
||||
|
||||
/* Indices into ws_escape */
|
||||
#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
|
||||
@@ -246,32 +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);
|
||||
|
||||
static inline void wordsplit_getwords (wordsplit_t *ws, size_t *wordc, char ***wordv)
|
||||
__attribute__ ((deprecated));
|
||||
|
||||
static inline void
|
||||
wordsplit_getwords (wordsplit_t *ws, size_t *wordc, char ***wordv)
|
||||
{
|
||||
wordsplit_get_words (ws, wordc, 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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2012-2025 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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2012-2025 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,7 @@
|
||||
# include <attr/xattr.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#ifndef ENOATTR
|
||||
# define ENOATTR ENODATA /* No such attribute */
|
||||
#endif
|
||||
@@ -50,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,
|
||||
@@ -62,7 +63,7 @@ ssize_t getxattrat (int dir_fd, const char *path, const char *name,
|
||||
ssize_t lgetxattrat (int dir_fd, const char *path, const char *name,
|
||||
void *value, size_t size);
|
||||
|
||||
/* dir-fd-relative listxattr. Obtain the list of extended attrubtes names. For
|
||||
/* dir-fd-relative listxattr. Obtain the list of extended attributes names. For
|
||||
more info see the listxattr(2) manpage. */
|
||||
ssize_t listxattrat (int dir_fd, const char *path, char *list, size_t size);
|
||||
|
||||
|
||||
2
paxutils
2
paxutils
Submodule paxutils updated: 56939847bf...063408cc6f
23
po/.gitignore
vendored
23
po/.gitignore
vendored
@@ -1,24 +1,23 @@
|
||||
/Makevars.template~
|
||||
/Makefile.in.in~
|
||||
/Makefile.in.in
|
||||
/Makevars.template
|
||||
/Rules-quot
|
||||
/boldquot.sed
|
||||
/en@boldquot.header
|
||||
/en@quot.header
|
||||
/insert-header.sin
|
||||
/quot.sed
|
||||
/remove-potcdate.sed
|
||||
/remove-potcdate.sin
|
||||
*.gmo
|
||||
*.mo
|
||||
*.po
|
||||
*~
|
||||
.reference
|
||||
LINGUAS
|
||||
Makefile
|
||||
Makefile.in
|
||||
Makefile.in.in
|
||||
Makevars
|
||||
Makevars.template
|
||||
POTFILES
|
||||
Rules-quot
|
||||
boldquot.sed
|
||||
en@boldquot.header
|
||||
en@quot.header
|
||||
insert-header.sed
|
||||
insert-header.sin
|
||||
quot.sed
|
||||
remove-potcdate.sed
|
||||
remove-potcdate.sin
|
||||
stamp-po
|
||||
tar.pot
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# List of files which contain translatable strings.
|
||||
|
||||
# Copyright 1996-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1996-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Make GNU tar scripts.
|
||||
|
||||
# Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ TAR=/bin/tar
|
||||
# (Optional) Path to rsh binary or its equivalent. You may wish to
|
||||
# set it to ssh as shown in the example below, to improve security.
|
||||
# In this case you will have to use public key authentication.
|
||||
RSH=/usr/local/bin/ssh
|
||||
RSH=/usr/bin/ssh
|
||||
|
||||
# (Optional) Path to rsh binary on remote mashines. This will be
|
||||
# (Optional) Path to rsh binary on remote machines. This will be
|
||||
# passed via --rsh-command option to the remote invocation of
|
||||
# tar
|
||||
RSH_COMMAND=/usr/local/bin/ssh
|
||||
RSH_COMMAND=/usr/bin/ssh
|
||||
|
||||
# Name of temporary file to hold volume numbers. This needs to be accessible
|
||||
# by all the machines which have filesystems to be dumped.
|
||||
@@ -43,7 +43,7 @@ BACKUP_DIRS='remote1:/etc remote1:/var/spool/crontab'
|
||||
# DIRLIST=/etc/my-backup/dirlist
|
||||
|
||||
# List of individual files to be dumped.
|
||||
# These should be accesible from the machine on which the dump is run.
|
||||
# These should be accessible from the machine on which the dump is run.
|
||||
BACKUP_FILES=''
|
||||
# This list may also be kept in file $SYSCONFDIR/backup/files, the
|
||||
# format of which is the same as described above. The location of
|
||||
@@ -82,7 +82,7 @@ SLEEP_MESSAGE="`awk '
|
||||
}' /dev/null`"
|
||||
|
||||
|
||||
# Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Make backups.
|
||||
|
||||
# Copyright 2004-2006, 2013 Free Software Foundation
|
||||
# Copyright 2004-2006, 2013, 2019 Free Software Foundation
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -149,7 +149,7 @@ message 20 "Variables:"
|
||||
message 20 "BACKUP_DIRS=$BACKUP_DIRS"
|
||||
message 20 "BACKUP_FILES=$BACKUP_FILES"
|
||||
|
||||
# The buch of commands below is run in a subshell for which all output is
|
||||
# The bunch of commands below is run in a subshell for which all output is
|
||||
# piped through 'tee' to the logfile. Doing this, instead of having
|
||||
# multiple pipelines all over the place, is cleaner and allows access to
|
||||
# the exit value from various commands more easily.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Make backups.
|
||||
|
||||
# Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 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 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Restore backups.
|
||||
|
||||
# Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -183,7 +183,7 @@ restore_files()
|
||||
done
|
||||
}
|
||||
|
||||
# Operation Overwiew:
|
||||
# Operation Overview:
|
||||
#
|
||||
# 1. Determine the time of the last backup
|
||||
# 2. Create list of incremental listings to process
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /usr/bin/perl -w
|
||||
# Display and edit the 'dev' field in tar's snapshots
|
||||
# Copyright 2007-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2007-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
|
||||
@@ -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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2004-2025 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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2006-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1994-2025 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU tar.
|
||||
|
||||
@@ -49,6 +49,8 @@ tar_SOURCES = \
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
|
||||
AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
|
||||
|
||||
LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
|
||||
|
||||
tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX)
|
||||
tar_LDADD = $(LIBS) ../lib/libtar.a ../gnu/libgnu.a\
|
||||
$(LIB_ACL) $(QCOPY_ACL_LIB) $(CLOCK_TIME_LIB) $(EUIDACCESS_LIBGEN)\
|
||||
$(GETRANDOM_LIB) $(HARD_LOCALE_LIB) $(FILE_HAS_ACL_LIB) $(MBRTOWC_LIB)\
|
||||
$(LIB_SELINUX) $(SETLOCALE_NULL_LIB) \
|
||||
$(LIBINTL) $(LIBICONV)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Long integers, for GNU tar.
|
||||
Copyright 1999-2019 Free Software Foundation, Inc.
|
||||
Copyright 1999-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
789
src/buffer.c
789
src/buffer.c
File diff suppressed because it is too large
Load Diff
222
src/checkpoint.c
222
src/checkpoint.c
@@ -1,6 +1,6 @@
|
||||
/* Checkpoint management for tar.
|
||||
|
||||
Copyright 2007-2019 Free Software Foundation, Inc.
|
||||
Copyright 2007-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
|
||||
#include <system.h>
|
||||
#include "common.h"
|
||||
#include "wordsplit.h"
|
||||
|
||||
#include <wordsplit.h>
|
||||
|
||||
#include <flexmember.h>
|
||||
#include <fprintftime.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include "fprintftime.h"
|
||||
#include <signal.h>
|
||||
|
||||
enum checkpoint_opcode
|
||||
@@ -47,103 +51,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 == '\'')
|
||||
&& 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);
|
||||
alloc_action (cop_dot, NULL);
|
||||
else if (strcmp (str, "bell") == 0)
|
||||
alloc_action (cop_bell);
|
||||
alloc_action (cop_bell, NULL);
|
||||
else if (strcmp (str, "echo") == 0)
|
||||
alloc_action (cop_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);
|
||||
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 +153,7 @@ checkpoint_finish_compile (void)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *checkpoint_total_format[] = {
|
||||
"R",
|
||||
"W",
|
||||
"D"
|
||||
};
|
||||
|
||||
static long
|
||||
static intmax_t
|
||||
getwidth (FILE *fp)
|
||||
{
|
||||
char const *columns;
|
||||
@@ -189,8 +167,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 +177,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, '}');
|
||||
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 +198,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 +238,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 +295,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 +303,37 @@ format_checkpoint_string (FILE *fp, size_t len,
|
||||
|
||||
case 't':
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
struct timespec ts = current_timespec ();
|
||||
const char *fmt = arg ? arg : "%c";
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
tm = localtime (&tv.tv_sec);
|
||||
len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
|
||||
struct tm *tm = localtime (&ts.tv_sec);
|
||||
len = add_printf (len,
|
||||
(tm ? fprintftime (fp, fmt, tm, 0, ts.tv_nsec)
|
||||
: fprintf (fp, "????""-??""-?? ??:??:??")));
|
||||
}
|
||||
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 +344,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 +408,7 @@ run_checkpoint_actions (bool do_write)
|
||||
break;
|
||||
|
||||
case cop_totals:
|
||||
compute_duration ();
|
||||
compute_duration_ns ();
|
||||
print_total_stats ();
|
||||
break;
|
||||
|
||||
@@ -447,7 +433,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);
|
||||
|
||||
693
src/common.h
693
src/common.h
File diff suppressed because it is too large
Load Diff
235
src/compare.c
235
src/compare.c
@@ -1,6 +1,6 @@
|
||||
/* Diff files from a tar archive.
|
||||
|
||||
Copyright 1988-2019 Free Software Foundation, Inc.
|
||||
Copyright 1988-2025 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,21 +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 (size_t size __attribute__ ((unused)),
|
||||
char *data __attribute__ ((unused)))
|
||||
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);
|
||||
@@ -94,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 (memcmp (buffer, diff_buffer, bytes) != 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -149,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,8 +223,6 @@ diff_file (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
int status;
|
||||
|
||||
if (current_stat_info.is_sparse)
|
||||
sparse_diff_file (diff_handle, ¤t_stat_info);
|
||||
else
|
||||
@@ -241,12 +233,11 @@ diff_file (void)
|
||||
{
|
||||
struct timespec atime = get_stat_atime (&stat_data);
|
||||
if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
|
||||
!= 0)
|
||||
< 0)
|
||||
utime_error (file_name);
|
||||
}
|
||||
|
||||
status = close (diff_handle);
|
||||
if (status != 0)
|
||||
if (close (diff_handle) < 0)
|
||||
close_error (file_name);
|
||||
}
|
||||
}
|
||||
@@ -261,19 +252,18 @@ 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,
|
||||
@@ -294,7 +284,6 @@ diff_symlink (void)
|
||||
if (linkbuf != buf)
|
||||
free (linkbuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
diff_special (void)
|
||||
@@ -330,37 +319,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;
|
||||
}
|
||||
@@ -376,7 +370,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);
|
||||
@@ -393,7 +387,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
|
||||
@@ -403,16 +397,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;
|
||||
|
||||
@@ -423,10 +414,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 ();
|
||||
@@ -434,7 +426,7 @@ diff_multivol (void)
|
||||
}
|
||||
|
||||
|
||||
fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
|
||||
int fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
@@ -452,8 +444,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);
|
||||
}
|
||||
|
||||
@@ -476,9 +467,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:
|
||||
@@ -497,11 +488,9 @@ diff_archive (void)
|
||||
diff_link ();
|
||||
break;
|
||||
|
||||
#ifdef HAVE_READLINK
|
||||
case SYMTYPE:
|
||||
diff_symlink ();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case CHRTYPE:
|
||||
case BLKTYPE:
|
||||
@@ -527,22 +516,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 ();
|
||||
|
||||
@@ -566,31 +553,12 @@ verify_volume (void)
|
||||
ioctl (archive, FDFLUSH);
|
||||
#endif
|
||||
|
||||
#ifdef MTIOCTOP
|
||||
{
|
||||
struct mtop operation;
|
||||
int status;
|
||||
|
||||
operation.mt_op = MTBSF;
|
||||
operation.mt_count = 1;
|
||||
if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
|
||||
{
|
||||
if (errno != EIO
|
||||
|| (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
|
||||
status < 0))
|
||||
{
|
||||
#endif
|
||||
if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
|
||||
{
|
||||
/* Lseek failed. Try a different method. */
|
||||
seek_warn (archive_name_array[0]);
|
||||
return;
|
||||
}
|
||||
#ifdef MTIOCTOP
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!mtioseek (true, -1) && rmtlseek (archive, 0, SEEK_SET) < 0)
|
||||
{
|
||||
/* Lseek failed. Try a different method. */
|
||||
seek_warn (archive_name_array[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
access_mode = ACCESS_READ;
|
||||
now_verifying = 1;
|
||||
@@ -604,7 +572,7 @@ verify_volume (void)
|
||||
|
||||
if (status == HEADER_FAILURE)
|
||||
{
|
||||
int counter = 0;
|
||||
intmax_t counter = 0;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -615,10 +583,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;
|
||||
@@ -627,20 +596,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);
|
||||
}
|
||||
|
||||
678
src/create.c
678
src/create.c
File diff suppressed because it is too large
Load Diff
326
src/delete.c
326
src/delete.c
@@ -1,6 +1,6 @@
|
||||
/* Delete entries from a tar archive.
|
||||
|
||||
Copyright 1988-2019 Free Software Foundation, Inc.
|
||||
Copyright 1988-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -18,24 +18,14 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <system.h>
|
||||
#include <system-ioctl.h>
|
||||
|
||||
#include "common.h"
|
||||
#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;
|
||||
@@ -50,47 +40,37 @@ move_archive (off_t count)
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
#ifdef MTIOCTOP
|
||||
{
|
||||
struct mtop operation;
|
||||
|
||||
if (count < 0
|
||||
? (operation.mt_op = MTBSR,
|
||||
operation.mt_count = -count,
|
||||
operation.mt_count == -count)
|
||||
: (operation.mt_op = MTFSR,
|
||||
operation.mt_count = count,
|
||||
operation.mt_count == count))
|
||||
{
|
||||
if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
|
||||
return;
|
||||
|
||||
if (errno == EIO
|
||||
&& 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* MTIOCTOP */
|
||||
|
||||
{
|
||||
off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR);
|
||||
off_t increment = record_size * (off_t) count;
|
||||
off_t position = position0 + increment;
|
||||
|
||||
if (increment / count != record_size
|
||||
|| (position < position0) != (increment < 0)
|
||||
|| (position = position < 0 ? 0 : position,
|
||||
rmtlseek (archive, position, SEEK_SET) != position))
|
||||
seek_error_details (archive_name_array[0], position);
|
||||
|
||||
if (mtioseek (false, count))
|
||||
return;
|
||||
}
|
||||
|
||||
off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
|
||||
if (0 <= position0)
|
||||
{
|
||||
/* Pretend the starting position is at the first record
|
||||
boundary after POSITION0. This is useful at EOF after
|
||||
a short read. */
|
||||
idx_t short_size = position0 % record_size;
|
||||
idx_t start_offset = short_size ? record_size - short_size : 0;
|
||||
off_t increment, move_start;
|
||||
if (ckd_mul (&increment, record_size, count)
|
||||
|| ckd_add (&move_start, position0, start_offset)
|
||||
|| ckd_add (&position, move_start, increment)
|
||||
|| position < 0)
|
||||
{
|
||||
paxerror (EOVERFLOW, "lseek: %s", archive_name_array[0]);
|
||||
return;
|
||||
}
|
||||
else if (rmtlseek (archive, position, SEEK_SET) == position)
|
||||
return;
|
||||
}
|
||||
if (!_isrmt (archive))
|
||||
seek_error_details (archive_name_array[0], position);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -121,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);
|
||||
@@ -144,7 +123,25 @@ 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)
|
||||
{
|
||||
set_next_block_after (current_header);
|
||||
off_t size = current_stat_info.stat.st_size;
|
||||
off_t blocks_to_skip = (size >> LG_BLOCKSIZE) + !!(size & (BLOCKSIZE - 1));
|
||||
|
||||
while (record_end - current_block <= blocks_to_skip)
|
||||
{
|
||||
blocks_to_skip -= (record_end - current_block);
|
||||
flush_archive ();
|
||||
if (record_end == current_block)
|
||||
/* Hit EOF */
|
||||
return;
|
||||
}
|
||||
current_block += blocks_to_skip;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -155,19 +152,19 @@ delete_archive_members (void)
|
||||
|
||||
/* FIXME: Should clean the routine before cleaning these variables :-( */
|
||||
struct name *name;
|
||||
off_t blocks_to_skip = 0;
|
||||
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;
|
||||
|
||||
/* Skip to the first member that matches the name list. */
|
||||
do
|
||||
{
|
||||
enum read_header status = read_header (¤t_header,
|
||||
¤t_stat_info,
|
||||
read_header_x_raw);
|
||||
¤t_stat_info,
|
||||
read_header_x_raw);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
@@ -175,15 +172,15 @@ delete_archive_members (void)
|
||||
abort ();
|
||||
|
||||
case HEADER_SUCCESS:
|
||||
if ((name = name_scan (current_stat_info.file_name)) == NULL)
|
||||
if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
|
||||
{
|
||||
skip_member ();
|
||||
skim_member (acting_as_filter);
|
||||
break;
|
||||
}
|
||||
name->found_count++;
|
||||
if (!ISFOUND(name))
|
||||
if (!isfound (name))
|
||||
{
|
||||
skip_member ();
|
||||
skim_member (acting_as_filter);
|
||||
break;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
@@ -207,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;
|
||||
@@ -243,15 +240,12 @@ delete_archive_members (void)
|
||||
|
||||
if (logical_status == HEADER_SUCCESS)
|
||||
{
|
||||
/* FIXME: Pheew! This is crufty code! */
|
||||
logical_status = HEADER_STILL_UNREAD;
|
||||
goto flush_file;
|
||||
flush_file ();
|
||||
}
|
||||
|
||||
/* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
|
||||
"delete.c", line 223: warning: loop not entered at top
|
||||
Reported by Bruno Haible. */
|
||||
while (1)
|
||||
/* Skip matching members and move the rest up the archive. */
|
||||
while (logical_status != HEADER_END_OF_FILE)
|
||||
{
|
||||
enum read_header status;
|
||||
|
||||
@@ -259,105 +253,109 @@ delete_archive_members (void)
|
||||
|
||||
if (current_block == record_end)
|
||||
flush_archive ();
|
||||
|
||||
status = read_header (¤t_header, ¤t_stat_info,
|
||||
read_header_auto);
|
||||
read_header_auto);
|
||||
|
||||
xheader_decode (¤t_stat_info);
|
||||
|
||||
if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
|
||||
switch (status)
|
||||
{
|
||||
case HEADER_STILL_UNREAD:
|
||||
case HEADER_SUCCESS_EXTENDED:
|
||||
abort ();
|
||||
|
||||
case HEADER_SUCCESS:
|
||||
/* Found another header. */
|
||||
xheader_decode (¤t_stat_info);
|
||||
|
||||
if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
|
||||
{
|
||||
name->found_count++;
|
||||
if (isfound (name))
|
||||
{
|
||||
flush_file ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Copy header. */
|
||||
|
||||
if (current_stat_info.xhdr.size)
|
||||
{
|
||||
write_recent_bytes (current_stat_info.xhdr.buffer,
|
||||
current_stat_info.xhdr.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_recent_blocks (recent_long_name,
|
||||
recent_long_name_blocks);
|
||||
write_recent_blocks (recent_long_link,
|
||||
recent_long_link_blocks);
|
||||
}
|
||||
new_record[new_blocks] = *current_header;
|
||||
new_blocks++;
|
||||
blocks_to_keep
|
||||
= ((current_stat_info.stat.st_size >> LG_BLOCKSIZE)
|
||||
+ !!(current_stat_info.stat.st_size & (BLOCKSIZE - 1)));
|
||||
set_next_block_after (current_header);
|
||||
continue;
|
||||
}
|
||||
if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
|
||||
{
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (true);
|
||||
|
||||
/* Copy data. */
|
||||
|
||||
kept_blocks_in_record = record_end - current_block;
|
||||
if (kept_blocks_in_record > blocks_to_keep)
|
||||
kept_blocks_in_record = blocks_to_keep;
|
||||
|
||||
while (blocks_to_keep)
|
||||
{
|
||||
ptrdiff_t count;
|
||||
|
||||
if (current_block == record_end)
|
||||
{
|
||||
flush_read ();
|
||||
current_block = record_start;
|
||||
kept_blocks_in_record = blocking_factor;
|
||||
if (kept_blocks_in_record > blocks_to_keep)
|
||||
kept_blocks_in_record = blocks_to_keep;
|
||||
}
|
||||
count = kept_blocks_in_record;
|
||||
if (blocking_factor - new_blocks < count)
|
||||
count = blocking_factor - new_blocks;
|
||||
|
||||
if (! count)
|
||||
abort ();
|
||||
|
||||
memcpy (new_record + new_blocks, current_block,
|
||||
count * BLOCKSIZE);
|
||||
new_blocks += count;
|
||||
current_block += count;
|
||||
blocks_to_keep -= count;
|
||||
kept_blocks_in_record -= count;
|
||||
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (true);
|
||||
}
|
||||
break;
|
||||
|
||||
case HEADER_ZERO_BLOCK:
|
||||
if (ignore_zeros_option)
|
||||
set_next_block_after (current_header);
|
||||
else
|
||||
logical_status = HEADER_END_OF_FILE;
|
||||
break;
|
||||
|
||||
case HEADER_END_OF_FILE:
|
||||
logical_status = HEADER_END_OF_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == HEADER_FAILURE)
|
||||
{
|
||||
ERROR ((0, 0, _("Deleting non-header from archive")));
|
||||
case HEADER_FAILURE:
|
||||
paxerror (0, _("Deleting non-header from archive"));
|
||||
set_next_block_after (current_header);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Found another header. */
|
||||
|
||||
if ((name = name_scan (current_stat_info.file_name)) != NULL)
|
||||
{
|
||||
name->found_count++;
|
||||
if (ISFOUND(name))
|
||||
{
|
||||
flush_file:
|
||||
set_next_block_after (current_header);
|
||||
blocks_to_skip = (current_stat_info.stat.st_size
|
||||
+ BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
|
||||
while (record_end - current_block <= blocks_to_skip)
|
||||
{
|
||||
blocks_to_skip -= (record_end - current_block);
|
||||
flush_archive ();
|
||||
}
|
||||
current_block += blocks_to_skip;
|
||||
blocks_to_skip = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Copy header. */
|
||||
|
||||
if (current_stat_info.xhdr.size)
|
||||
{
|
||||
write_recent_bytes (current_stat_info.xhdr.buffer,
|
||||
current_stat_info.xhdr.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_recent_blocks (recent_long_name, recent_long_name_blocks);
|
||||
write_recent_blocks (recent_long_link, recent_long_link_blocks);
|
||||
}
|
||||
new_record[new_blocks] = *current_header;
|
||||
new_blocks++;
|
||||
blocks_to_keep
|
||||
= (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
|
||||
set_next_block_after (current_header);
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
|
||||
/* Copy data. */
|
||||
|
||||
kept_blocks_in_record = record_end - current_block;
|
||||
if (kept_blocks_in_record > blocks_to_keep)
|
||||
kept_blocks_in_record = blocks_to_keep;
|
||||
|
||||
while (blocks_to_keep)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (current_block == record_end)
|
||||
{
|
||||
flush_read ();
|
||||
current_block = record_start;
|
||||
kept_blocks_in_record = blocking_factor;
|
||||
if (kept_blocks_in_record > blocks_to_keep)
|
||||
kept_blocks_in_record = blocks_to_keep;
|
||||
}
|
||||
count = kept_blocks_in_record;
|
||||
if (blocking_factor - new_blocks < count)
|
||||
count = blocking_factor - new_blocks;
|
||||
|
||||
if (! count)
|
||||
abort ();
|
||||
|
||||
memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE);
|
||||
new_blocks += count;
|
||||
current_block += count;
|
||||
blocks_to_keep -= count;
|
||||
kept_blocks_in_record -= count;
|
||||
|
||||
if (new_blocks == blocking_factor)
|
||||
write_record (1);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
tar_stat_destroy (¤t_stat_info);
|
||||
}
|
||||
|
||||
if (logical_status == HEADER_END_OF_FILE)
|
||||
@@ -365,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);
|
||||
@@ -379,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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2014-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
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>
|
||||
#include <wordsplit.h>
|
||||
#include "common.h"
|
||||
@@ -40,7 +42,7 @@ struct excfile
|
||||
{
|
||||
struct excfile *next;
|
||||
int flags;
|
||||
char name[1];
|
||||
char name[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
|
||||
static struct excfile *excfile_head, *excfile_tail;
|
||||
@@ -48,7 +50,8 @@ static struct excfile *excfile_head, *excfile_tail;
|
||||
void
|
||||
excfile_add (const char *name, int flags)
|
||||
{
|
||||
struct excfile *p = xmalloc (sizeof (*p) + strlen (name));
|
||||
struct excfile *p = xmalloc (FLEXNSIZEOF (struct excfile, name,
|
||||
strlen (name) + 1));
|
||||
p->next = NULL;
|
||||
p->flags = flags;
|
||||
strcpy (p->name, name);
|
||||
@@ -69,53 +72,45 @@ 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);
|
||||
|
||||
if (add_exclude_fp (vcsfile->addfn, ex, fp,
|
||||
EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n',
|
||||
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;
|
||||
|
||||
@@ -197,23 +192,26 @@ excluded_name (char const *name, struct tar_stat_info *st)
|
||||
}
|
||||
|
||||
static void
|
||||
cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
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;
|
||||
@@ -223,9 +221,10 @@ git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
}
|
||||
|
||||
static void
|
||||
bzr_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
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;
|
||||
@@ -260,15 +259,14 @@ 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)
|
||||
/* FIXME: Regexps must be perl-style */
|
||||
@@ -279,7 +277,7 @@ hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
len = strlen(pattern);
|
||||
idx_t len = strlen (pattern);
|
||||
if (pattern[len-1] == '/')
|
||||
{
|
||||
char *p;
|
||||
@@ -325,5 +323,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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2009-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
|
||||
1192
src/extract.c
1192
src/extract.c
File diff suppressed because it is too large
Load Diff
679
src/incremen.c
679
src/incremen.c
File diff suppressed because it is too large
Load Diff
650
src/list.c
650
src/list.c
File diff suppressed because it is too large
Load Diff
116
src/map.c
116
src/map.c
@@ -1,6 +1,6 @@
|
||||
/* Owner/group mapping for tar
|
||||
|
||||
Copyright 2015-2019 Free Software Foundation, Inc.
|
||||
Copyright 2015-2025 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,7 +167,7 @@ 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)))
|
||||
&& hash_insert (*ptab, ent)))
|
||||
@@ -179,7 +177,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 +197,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 +238,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;
|
||||
}
|
||||
|
||||
486
src/misc.c
486
src/misc.c
@@ -1,6 +1,6 @@
|
||||
/* Miscellaneous functions, not really specific to GNU tar.
|
||||
|
||||
Copyright 1988-2019 Free Software Foundation, Inc.
|
||||
Copyright 1988-2025 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
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <system.h>
|
||||
#include <rmt.h>
|
||||
#include "common.h"
|
||||
#include <c-ctype.h>
|
||||
#include <quotearg.h>
|
||||
#include <xgetcwd.h>
|
||||
#include <unlinkdir.h>
|
||||
@@ -30,7 +31,7 @@
|
||||
|
||||
static void namebuf_add_dir (namebuf_t, char const *);
|
||||
static char *namebuf_finish (namebuf_t);
|
||||
static const char *tar_getcdpath (int);
|
||||
static const char *tar_getcdpath (idx_t);
|
||||
|
||||
char const *
|
||||
quote_n_colon (int n, char const *arg)
|
||||
@@ -43,24 +44,43 @@ quote_n_colon (int n, char const *arg)
|
||||
/* Assign STRING to a copy of VALUE if not zero, or to zero. If
|
||||
STRING was nonzero, it is freed first. */
|
||||
void
|
||||
assign_string (char **string, const char *value)
|
||||
assign_string_or_null (char **string, const char *value)
|
||||
{
|
||||
free (*string);
|
||||
*string = value ? xstrdup (value) : 0;
|
||||
if (value)
|
||||
assign_string (string, value);
|
||||
else
|
||||
assign_null (string);
|
||||
}
|
||||
|
||||
void
|
||||
assign_string_n (char **string, const char *value, size_t n)
|
||||
assign_string (char **string, const char *value)
|
||||
{
|
||||
free (*string);
|
||||
*string = xstrdup (value);
|
||||
}
|
||||
|
||||
void
|
||||
assign_null (char **string)
|
||||
{
|
||||
char *old = *string;
|
||||
*string = NULL;
|
||||
free (old);
|
||||
}
|
||||
|
||||
void
|
||||
assign_string_n (char **string, const char *value, idx_t n)
|
||||
{
|
||||
free (*string);
|
||||
if (value)
|
||||
{
|
||||
size_t l = strnlen (value, n);
|
||||
idx_t l = strnlen (value, n);
|
||||
char *p = xmalloc (l + 1);
|
||||
memcpy (p, value, l);
|
||||
p[l] = 0;
|
||||
*string = p;
|
||||
}
|
||||
else
|
||||
*string = NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -84,20 +104,20 @@ quote_copy_string (const char *string)
|
||||
const char *source = string;
|
||||
char *destination = 0;
|
||||
char *buffer = 0;
|
||||
int copying = 0;
|
||||
bool copying = false;
|
||||
|
||||
while (*source)
|
||||
{
|
||||
int character = *source++;
|
||||
char character = *source++;
|
||||
|
||||
switch (character)
|
||||
{
|
||||
case '\n': case '\\':
|
||||
if (!copying)
|
||||
{
|
||||
size_t length = (source - string) - 1;
|
||||
idx_t length = (source - string) - 1;
|
||||
|
||||
copying = 1;
|
||||
copying = true;
|
||||
buffer = xmalloc (length + 2 + 2 * strlen (source) + 1);
|
||||
memcpy (buffer, string, length);
|
||||
destination = buffer + length;
|
||||
@@ -121,20 +141,18 @@ quote_copy_string (const char *string)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Takes a quoted C string (like those produced by quote_copy_string)
|
||||
and turns it back into the un-quoted original. This is done in
|
||||
place. Returns 0 only if the string was not properly quoted, but
|
||||
completes the unquoting anyway.
|
||||
/* Take a quoted C string (like those produced by quote_copy_string)
|
||||
and turn it back into the un-quoted original, in place.
|
||||
Complete the unquoting even if the string was not properly quoted.
|
||||
|
||||
This is used for reading the saved directory file in incremental
|
||||
dumps. It is used for decoding old 'N' records (demangling names).
|
||||
But also, it is used for decoding file arguments, would they come
|
||||
from the shell or a -T file, and for decoding the --exclude
|
||||
argument. */
|
||||
int
|
||||
void
|
||||
unquote_string (char *string)
|
||||
{
|
||||
int result = 1;
|
||||
char *source = string;
|
||||
char *destination = string;
|
||||
|
||||
@@ -201,26 +219,24 @@ unquote_string (char *string)
|
||||
case '6':
|
||||
case '7':
|
||||
{
|
||||
int value = *source++ - '0';
|
||||
unsigned char value = *source++ - '0';
|
||||
|
||||
if (*source < '0' || *source > '7')
|
||||
{
|
||||
*destination++ = value;
|
||||
break;
|
||||
}
|
||||
value = value * 8 + *source++ - '0';
|
||||
if (*source < '0' || *source > '7')
|
||||
unsigned char val1 = value * 8 + (*source++ - '0'), val2;
|
||||
if (*source < '0' || *source > '7' || ckd_mul (&val2, val1, 8))
|
||||
{
|
||||
*destination++ = value;
|
||||
break;
|
||||
}
|
||||
value = value * 8 + *source++ - '0';
|
||||
*destination++ = value;
|
||||
*destination++ = val2 + (*source++ - '0');
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
result = 0;
|
||||
*destination++ = '\\';
|
||||
if (*source)
|
||||
*destination++ = *source++;
|
||||
@@ -233,7 +249,6 @@ unquote_string (char *string)
|
||||
|
||||
if (source != destination)
|
||||
*destination = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Zap trailing slashes. */
|
||||
@@ -295,7 +310,7 @@ normalize_filename_x (char *file_name)
|
||||
Return a normalized newly-allocated copy. */
|
||||
|
||||
char *
|
||||
normalize_filename (int cdidx, const char *name)
|
||||
normalize_filename (idx_t cdidx, const char *name)
|
||||
{
|
||||
char *copy = NULL;
|
||||
|
||||
@@ -311,11 +326,9 @@ normalize_filename (int cdidx, const char *name)
|
||||
should use dev+ino pairs instead of names? (See listed03.at for
|
||||
a related test case.) */
|
||||
const char *cdpath = tar_getcdpath (cdidx);
|
||||
size_t copylen;
|
||||
idx_t copylen;
|
||||
bool need_separator;
|
||||
|
||||
if (!cdpath)
|
||||
call_arg_fatal ("getcwd", ".");
|
||||
copylen = strlen (cdpath);
|
||||
need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
|
||||
&& copylen == 2 && ISSLASH (cdpath[1]));
|
||||
@@ -333,11 +346,11 @@ normalize_filename (int cdidx, const char *name)
|
||||
|
||||
|
||||
void
|
||||
replace_prefix (char **pname, const char *samp, size_t slen,
|
||||
const char *repl, size_t rlen)
|
||||
replace_prefix (char **pname, const char *samp, idx_t slen,
|
||||
const char *repl, idx_t rlen)
|
||||
{
|
||||
char *name = *pname;
|
||||
size_t nlen = strlen (name);
|
||||
idx_t nlen = strlen (name);
|
||||
if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
|
||||
{
|
||||
if (rlen > slen)
|
||||
@@ -354,18 +367,17 @@ replace_prefix (char **pname, const char *samp, size_t slen,
|
||||
/* Handling numbers. */
|
||||
|
||||
/* Convert VALUE, which is converted from a system integer type whose
|
||||
minimum value is MINVAL and maximum MINVAL, to an decimal
|
||||
minimum value is MINVAL and maximum MINVAL, to a decimal
|
||||
integer string. Use the storage in BUF and return a pointer to the
|
||||
converted string. If VALUE is converted from a negative integer in
|
||||
the range MINVAL .. -1, represent it with a string representation
|
||||
of the negative integer, using leading '-'. */
|
||||
#if ! (INTMAX_MAX <= UINTMAX_MAX / 2)
|
||||
# error "sysinttostr: uintmax_t cannot represent all intmax_t values"
|
||||
#endif
|
||||
char *
|
||||
sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval,
|
||||
char buf[SYSINT_BUFSIZE])
|
||||
{
|
||||
static_assert (INTMAX_MAX <= UINTMAX_MAX / 2);
|
||||
|
||||
if (value <= maxval)
|
||||
return umaxtostr (value, buf);
|
||||
else
|
||||
@@ -375,52 +387,110 @@ sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval,
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a prefix of the string ARG to a system integer type whose
|
||||
minimum value is MINVAL and maximum MAXVAL. If MINVAL is negative,
|
||||
/* Convert T to a decimal integer string. Use the storage in BUF and
|
||||
return a pointer to the converted string. */
|
||||
char *
|
||||
timetostr (time_t t, char buf[SYSINT_BUFSIZE])
|
||||
{
|
||||
return sysinttostr (t, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), buf);
|
||||
}
|
||||
|
||||
/* Convert a prefix of the string ARG to a system integer type.
|
||||
If ARGLIM, set *ARGLIM to point to just after the prefix.
|
||||
If OVERFLOW, set *OVERFLOW to true or false
|
||||
depending on whether the input is out of MINVAL..MAXVAL range.
|
||||
If the input is out of that range, return an extreme value.
|
||||
MINVAL must not be positive.
|
||||
|
||||
If MINVAL is negative, MAXVAL can be at most INTMAX_MAX, and
|
||||
negative integers MINVAL .. -1 are assumed to be represented using
|
||||
leading '-' in the usual way. If the represented value exceeds
|
||||
INTMAX_MAX, return a negative integer V such that (uintmax_t) V
|
||||
yields the represented value. If ARGLIM is nonnull, store into
|
||||
*ARGLIM a pointer to the first character after the prefix.
|
||||
yields the represented value.
|
||||
|
||||
On conversion error: if ARGLIM set *ARGLIM = ARG; if OVERFLOW set
|
||||
*OVERFLOW = false; then return 0.
|
||||
|
||||
This is the inverse of sysinttostr.
|
||||
|
||||
On a normal return, set errno = 0.
|
||||
On conversion error, return 0 and set errno = EINVAL.
|
||||
On overflow, return an extreme value and set errno = ERANGE. */
|
||||
#if ! (INTMAX_MAX <= UINTMAX_MAX)
|
||||
# error "strtosysint: nonnegative intmax_t does not fit in uintmax_t"
|
||||
#endif
|
||||
Sample call to this function:
|
||||
|
||||
char *s_end;
|
||||
bool overflow;
|
||||
idx_t i = stoint (s, &s_end, &overflow, 0, IDX_MAX);
|
||||
if ((s_end == s) | *s_end | overflow)
|
||||
diagnose_invalid (s);
|
||||
|
||||
This example uses "|" instead of "||" for fewer branches at runtime,
|
||||
which tends to be more efficient on modern processors.
|
||||
|
||||
This function is named "stoint" instead of "strtoint" because
|
||||
<string.h> reserves names beginning with "str". */
|
||||
|
||||
intmax_t
|
||||
strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval)
|
||||
stoint (char const *arg, char **arglim, bool *overflow,
|
||||
intmax_t minval, uintmax_t maxval)
|
||||
{
|
||||
errno = 0;
|
||||
if (maxval <= INTMAX_MAX)
|
||||
static_assert (INTMAX_MAX <= UINTMAX_MAX);
|
||||
char const *p = arg;
|
||||
intmax_t i;
|
||||
bool v = false;
|
||||
|
||||
if (c_isdigit (*p))
|
||||
{
|
||||
if (ISDIGIT (arg[*arg == '-']))
|
||||
if (minval < 0)
|
||||
{
|
||||
intmax_t i = strtoimax (arg, arglim, 10);
|
||||
intmax_t imaxval = maxval;
|
||||
if (minval <= i && i <= imaxval)
|
||||
return i;
|
||||
errno = ERANGE;
|
||||
return i < minval ? minval : maxval;
|
||||
i = *p - '0';
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&i, i, 10);
|
||||
v |= ckd_add (&i, i, *p - '0');
|
||||
}
|
||||
|
||||
v |= maxval < i;
|
||||
if (v)
|
||||
i = maxval;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t u = *p - '0';
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&u, u, 10);
|
||||
v |= ckd_add (&u, u, *p - '0');
|
||||
}
|
||||
|
||||
v |= maxval < u;
|
||||
if (v)
|
||||
u = maxval;
|
||||
i = represent_uintmax (u);
|
||||
}
|
||||
}
|
||||
else if (minval < 0 && *p == '-' && c_isdigit (p[1]))
|
||||
{
|
||||
p++;
|
||||
i = - (*p - '0');
|
||||
|
||||
while (c_isdigit (*++p))
|
||||
{
|
||||
v |= ckd_mul (&i, i, 10);
|
||||
v |= ckd_sub (&i, i, *p - '0');
|
||||
}
|
||||
|
||||
v |= i < minval;
|
||||
if (v)
|
||||
i = minval;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ISDIGIT (*arg))
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, arglim, 10);
|
||||
if (i <= maxval)
|
||||
return represent_uintmax (i);
|
||||
errno = ERANGE;
|
||||
return maxval;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
if (arglim)
|
||||
*arglim = (char *) p;
|
||||
if (overflow)
|
||||
*overflow = v;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Output fraction and trailing digits appropriate for a nanoseconds
|
||||
@@ -456,11 +526,10 @@ code_ns_fraction (int ns, char *p)
|
||||
}
|
||||
|
||||
char const *
|
||||
code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
code_timespec (struct timespec t, char tsbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
{
|
||||
time_t s = t.tv_sec;
|
||||
int ns = t.tv_nsec;
|
||||
char *np;
|
||||
bool negative = s < 0;
|
||||
|
||||
/* ignore invalid values of ns */
|
||||
@@ -473,46 +542,24 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
|
||||
ns = BILLION - ns;
|
||||
}
|
||||
|
||||
np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
|
||||
if (negative)
|
||||
*--np = '-';
|
||||
code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
|
||||
return np;
|
||||
bool minus_zero = negative & !s;
|
||||
char *sstr = timetostr (s, tsbuf + 1);
|
||||
sstr[-1] = '-';
|
||||
sstr -= minus_zero;
|
||||
code_ns_fraction (ns, sstr + strlen (sstr));
|
||||
return sstr;
|
||||
}
|
||||
|
||||
struct timespec
|
||||
decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
|
||||
{
|
||||
time_t s = TYPE_MINIMUM (time_t);
|
||||
int ns = -1;
|
||||
char const *p = arg;
|
||||
bool negative = *arg == '-';
|
||||
struct timespec r;
|
||||
|
||||
if (! ISDIGIT (arg[negative]))
|
||||
errno = EINVAL;
|
||||
else
|
||||
bool overflow;
|
||||
time_t s = stoint (arg, arg_lim, &overflow,
|
||||
TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t));
|
||||
char const *p = *arg_lim;
|
||||
if (p != arg)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
if (negative)
|
||||
{
|
||||
intmax_t i = strtoimax (arg, arg_lim, 10);
|
||||
if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i)
|
||||
s = i;
|
||||
else
|
||||
errno = ERANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t i = strtoumax (arg, arg_lim, 10);
|
||||
if (i <= TYPE_MAXIMUM (time_t))
|
||||
s = i;
|
||||
else
|
||||
errno = ERANGE;
|
||||
}
|
||||
|
||||
p = *arg_lim;
|
||||
ns = 0;
|
||||
|
||||
if (parse_fraction && *p == '.')
|
||||
@@ -520,42 +567,33 @@ decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
|
||||
int digits = 0;
|
||||
bool trailing_nonzero = false;
|
||||
|
||||
while (ISDIGIT (*++p))
|
||||
while (c_isdigit (*++p))
|
||||
if (digits < LOG10_BILLION)
|
||||
digits++, ns = 10 * ns + (*p - '0');
|
||||
else
|
||||
trailing_nonzero |= *p != '0';
|
||||
|
||||
*arg_lim = (char *) p;
|
||||
|
||||
while (digits < LOG10_BILLION)
|
||||
digits++, ns *= 10;
|
||||
|
||||
if (negative)
|
||||
if (*arg == '-')
|
||||
{
|
||||
/* Convert "-1.10000000000001" to s == -2, ns == 89999999.
|
||||
I.e., truncate time stamps towards minus infinity while
|
||||
converting them to internal form. */
|
||||
ns += trailing_nonzero;
|
||||
if (ns != 0)
|
||||
{
|
||||
if (s == TYPE_MINIMUM (time_t))
|
||||
ns = -1;
|
||||
else
|
||||
{
|
||||
s--;
|
||||
ns = BILLION - ns;
|
||||
}
|
||||
}
|
||||
ns = ckd_sub (&s, s, 1) ? -1 : BILLION - ns;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
if (overflow)
|
||||
ns = -1;
|
||||
}
|
||||
|
||||
*arg_lim = (char *) p;
|
||||
r.tv_sec = s;
|
||||
r.tv_nsec = ns;
|
||||
return r;
|
||||
return (struct timespec) { .tv_sec = s, .tv_nsec = ns };
|
||||
}
|
||||
|
||||
/* File handling. */
|
||||
@@ -664,9 +702,9 @@ remove_any_file (const char *file_name, enum remove_option option)
|
||||
|
||||
case RECURSIVE_REMOVE_OPTION:
|
||||
{
|
||||
char *directory = tar_savedir (file_name, 0);
|
||||
char *directory = tar_savedir (file_name, false);
|
||||
char const *entry;
|
||||
size_t entrylen;
|
||||
idx_t entrylen;
|
||||
|
||||
if (! directory)
|
||||
return 0;
|
||||
@@ -678,13 +716,11 @@ remove_any_file (const char *file_name, enum remove_option option)
|
||||
char *file_name_buffer = make_file_name (file_name, entry);
|
||||
int r = remove_any_file (file_name_buffer,
|
||||
RECURSIVE_REMOVE_OPTION);
|
||||
int e = errno;
|
||||
free (file_name_buffer);
|
||||
|
||||
if (! r)
|
||||
{
|
||||
free (directory);
|
||||
errno = e;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -717,14 +753,14 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
|
||||
possible, real problems are unlikely. Doing any better would require a
|
||||
convention, GNU-wide, for all programs doing backups. */
|
||||
|
||||
assign_string (&after_backup_name, 0);
|
||||
assign_null (&after_backup_name);
|
||||
|
||||
/* Check if we really need to backup the file. */
|
||||
|
||||
if (this_is_the_archive && _remdev (file_name))
|
||||
return true;
|
||||
|
||||
if (deref_stat (file_name, &file_stat) != 0)
|
||||
if (deref_stat (file_name, &file_stat) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return true;
|
||||
@@ -757,10 +793,10 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
|
||||
{
|
||||
/* The backup operation failed. */
|
||||
int e = errno;
|
||||
ERROR ((0, e, _("%s: Cannot rename to %s"),
|
||||
quotearg_colon (before_backup_name),
|
||||
quote_n (1, after_backup_name)));
|
||||
assign_string (&after_backup_name, 0);
|
||||
paxerror (e, _("%s: Cannot rename to %s"),
|
||||
quotearg_colon (before_backup_name),
|
||||
quote_n (1, after_backup_name));
|
||||
assign_null (&after_backup_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -773,18 +809,18 @@ undo_last_backup (void)
|
||||
if (after_backup_name)
|
||||
{
|
||||
if (renameat (chdir_fd, after_backup_name, chdir_fd, before_backup_name)
|
||||
!= 0)
|
||||
< 0)
|
||||
{
|
||||
int e = errno;
|
||||
ERROR ((0, e, _("%s: Cannot rename to %s"),
|
||||
quotearg_colon (after_backup_name),
|
||||
quote_n (1, before_backup_name)));
|
||||
paxerror (e, _("%s: Cannot rename to %s"),
|
||||
quotearg_colon (after_backup_name),
|
||||
quote_n (1, before_backup_name));
|
||||
}
|
||||
if (verbose_option)
|
||||
fprintf (stdlis, _("Renaming %s back to %s\n"),
|
||||
quote_n (0, after_backup_name),
|
||||
quote_n (1, before_backup_name));
|
||||
assign_string (&after_backup_name, 0);
|
||||
assign_null (&after_backup_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,21 +836,25 @@ deref_stat (char const *name, struct stat *buf)
|
||||
/* Read from FD into the buffer BUF with COUNT bytes. Attempt to fill
|
||||
BUF. Wait until input is available; this matters because files are
|
||||
opened O_NONBLOCK for security reasons, and on some file systems
|
||||
this can cause read to fail with errno == EAGAIN. Return the
|
||||
actual number of bytes read, zero for EOF, or
|
||||
SAFE_READ_ERROR upon error. */
|
||||
size_t
|
||||
blocking_read (int fd, void *buf, size_t count)
|
||||
this can cause read to fail with errno == EAGAIN.
|
||||
If returning less than COUNT, set errno to indicate the error
|
||||
except set errno = 0 to indicate EOF. */
|
||||
idx_t
|
||||
blocking_read (int fd, void *buf, idx_t count)
|
||||
{
|
||||
size_t bytes = safe_read (fd, buf, count);
|
||||
idx_t bytes = full_read (fd, buf, count);
|
||||
|
||||
#if defined F_SETFL && O_NONBLOCK
|
||||
if (bytes == SAFE_READ_ERROR && errno == EAGAIN)
|
||||
if (bytes < count && errno == EAGAIN)
|
||||
{
|
||||
int flags = fcntl (fd, F_GETFL);
|
||||
if (0 <= flags && flags & O_NONBLOCK
|
||||
&& fcntl (fd, F_SETFL, flags & ~O_NONBLOCK) != -1)
|
||||
bytes = safe_read (fd, buf, count);
|
||||
{
|
||||
char *cbuf = buf;
|
||||
count -= bytes;
|
||||
bytes += full_read (fd, cbuf + bytes, count);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -826,11 +866,11 @@ blocking_read (int fd, void *buf, size_t count)
|
||||
files are opened O_NONBLOCK for security reasons, and on some file
|
||||
systems this can cause write to fail with errno == EAGAIN. Return
|
||||
the actual number of bytes written, setting errno if that is less
|
||||
than COUNT. */
|
||||
size_t
|
||||
blocking_write (int fd, void const *buf, size_t count)
|
||||
than COUNT. Return -1 on write error. */
|
||||
idx_t
|
||||
blocking_write (int fd, void const *buf, idx_t count)
|
||||
{
|
||||
size_t bytes = full_write (fd, buf, count);
|
||||
idx_t bytes = full_write (fd, buf, count);
|
||||
|
||||
#if defined F_SETFL && O_NONBLOCK
|
||||
if (bytes < count && errno == EAGAIN)
|
||||
@@ -879,10 +919,10 @@ struct wd
|
||||
static struct wd *wd;
|
||||
|
||||
/* The number of working directories in the vector. */
|
||||
static size_t wd_count;
|
||||
static idx_t wd_count;
|
||||
|
||||
/* The allocated size of the vector. */
|
||||
static size_t wd_alloc;
|
||||
static idx_t wd_alloc;
|
||||
|
||||
/* The maximum number of chdir targets with open directories.
|
||||
Don't make it too large, as many operating systems have a small
|
||||
@@ -892,36 +932,30 @@ enum { CHDIR_CACHE_SIZE = 16 };
|
||||
|
||||
/* Indexes into WD of chdir targets with open file descriptors, sorted
|
||||
most-recently used first. Zero indexes are unused. */
|
||||
static int wdcache[CHDIR_CACHE_SIZE];
|
||||
static idx_t wdcache[CHDIR_CACHE_SIZE];
|
||||
|
||||
/* Number of nonzero entries in WDCACHE. */
|
||||
static size_t wdcache_count;
|
||||
static idx_t wdcache_count;
|
||||
|
||||
int
|
||||
idx_t
|
||||
chdir_count (void)
|
||||
{
|
||||
if (wd_count == 0)
|
||||
return wd_count;
|
||||
return wd_count - 1;
|
||||
return wd_count - !!wd_count;
|
||||
}
|
||||
|
||||
/* DIR is the operand of a -C option; add it to vector of chdir targets,
|
||||
and return the index of its location. */
|
||||
int
|
||||
idx_t
|
||||
chdir_arg (char const *dir)
|
||||
{
|
||||
char *absdir;
|
||||
|
||||
if (wd_count == wd_alloc)
|
||||
{
|
||||
if (wd_alloc == 0)
|
||||
wd_alloc = 2;
|
||||
wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
|
||||
wd = xpalloc (wd, &wd_alloc, wd_alloc ? 1 : 2, -1, sizeof *wd);
|
||||
|
||||
if (! wd_count)
|
||||
{
|
||||
wd[wd_count].name = ".";
|
||||
wd[wd_count].abspath = xgetcwd ();
|
||||
wd[wd_count].abspath = NULL;
|
||||
wd[wd_count].fd = AT_FDCWD;
|
||||
wd_count++;
|
||||
}
|
||||
@@ -938,28 +972,14 @@ chdir_arg (char const *dir)
|
||||
return wd_count - 1;
|
||||
}
|
||||
|
||||
|
||||
/* If the given name is absolute, use it to represent this directory;
|
||||
otherwise, construct a name based on the previous -C option. */
|
||||
if (IS_ABSOLUTE_FILE_NAME (dir))
|
||||
absdir = xstrdup (dir);
|
||||
else if (wd[wd_count - 1].abspath)
|
||||
{
|
||||
namebuf_t nbuf = namebuf_create (wd[wd_count - 1].abspath);
|
||||
namebuf_add_dir (nbuf, dir);
|
||||
absdir = namebuf_finish (nbuf);
|
||||
}
|
||||
else
|
||||
absdir = 0;
|
||||
|
||||
wd[wd_count].name = dir;
|
||||
wd[wd_count].abspath = absdir;
|
||||
wd[wd_count].abspath = NULL;
|
||||
wd[wd_count].fd = 0;
|
||||
return wd_count++;
|
||||
}
|
||||
|
||||
/* Index of current directory. */
|
||||
int chdir_current;
|
||||
idx_t chdir_current;
|
||||
|
||||
/* Value suitable for use as the first argument to openat, and in
|
||||
similar locations for fstatat, etc. This is an open file
|
||||
@@ -973,7 +993,7 @@ int chdir_fd = AT_FDCWD;
|
||||
working directory; otherwise, I must be a value returned by
|
||||
chdir_arg. */
|
||||
void
|
||||
chdir_do (int i)
|
||||
chdir_do (idx_t i)
|
||||
{
|
||||
if (chdir_current != i)
|
||||
{
|
||||
@@ -998,7 +1018,7 @@ chdir_do (int i)
|
||||
else
|
||||
{
|
||||
struct wd *stale = &wd[wdcache[CHDIR_CACHE_SIZE - 1]];
|
||||
if (close (stale->fd) != 0)
|
||||
if (close (stale->fd) < 0)
|
||||
close_diag (stale->name);
|
||||
stale->fd = 0;
|
||||
wdcache[CHDIR_CACHE_SIZE - 1] = i;
|
||||
@@ -1009,11 +1029,11 @@ chdir_do (int i)
|
||||
{
|
||||
/* Move the i value to the front of the cache. This is
|
||||
O(CHDIR_CACHE_SIZE), but the cache is small. */
|
||||
size_t ci;
|
||||
int prev = wdcache[0];
|
||||
idx_t ci;
|
||||
idx_t prev = wdcache[0];
|
||||
for (ci = 1; prev != i; ci++)
|
||||
{
|
||||
int cur = wdcache[ci];
|
||||
idx_t cur = wdcache[ci];
|
||||
wdcache[ci] = prev;
|
||||
if (cur == i)
|
||||
break;
|
||||
@@ -1041,15 +1061,50 @@ tar_dirname (void)
|
||||
process's actual cwd. (Note that in this case IDX is ignored,
|
||||
since it should always be 0.) */
|
||||
static const char *
|
||||
tar_getcdpath (int idx)
|
||||
tar_getcdpath (idx_t idx)
|
||||
{
|
||||
if (!wd)
|
||||
{
|
||||
static char *cwd;
|
||||
if (!cwd)
|
||||
cwd = xgetcwd ();
|
||||
{
|
||||
cwd = xgetcwd ();
|
||||
if (!cwd)
|
||||
call_arg_fatal ("getcwd", ".");
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
|
||||
if (!wd[idx].abspath)
|
||||
{
|
||||
idx_t save_cwdi = chdir_current, i = idx;
|
||||
while (0 < i && !wd[i - 1].abspath)
|
||||
i--;
|
||||
|
||||
for (; i <= idx; i++)
|
||||
{
|
||||
chdir_do (i);
|
||||
if (i == 0)
|
||||
{
|
||||
if ((wd[i].abspath = xgetcwd ()) == NULL)
|
||||
call_arg_fatal ("getcwd", ".");
|
||||
}
|
||||
else if (IS_ABSOLUTE_FILE_NAME (wd[i].name))
|
||||
/* If the given name is absolute, use it to represent this
|
||||
directory; otherwise, construct a name based on the
|
||||
previous -C option. */
|
||||
wd[i].abspath = xstrdup (wd[i].name);
|
||||
else
|
||||
{
|
||||
namebuf_t nbuf = namebuf_create (wd[i - 1].abspath);
|
||||
namebuf_add_dir (nbuf, wd[i].name);
|
||||
wd[i].abspath = namebuf_finish (nbuf);
|
||||
}
|
||||
}
|
||||
|
||||
chdir_do (save_cwdi);
|
||||
}
|
||||
|
||||
return wd[idx].abspath;
|
||||
}
|
||||
|
||||
@@ -1058,7 +1113,7 @@ close_diag (char const *name)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
close_warn (name);
|
||||
}
|
||||
else
|
||||
@@ -1070,7 +1125,7 @@ open_diag (char const *name)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
open_warn (name);
|
||||
}
|
||||
else
|
||||
@@ -1078,11 +1133,11 @@ open_diag (char const *name)
|
||||
}
|
||||
|
||||
void
|
||||
read_diag_details (char const *name, off_t offset, size_t size)
|
||||
read_diag_details (char const *name, off_t offset, idx_t size)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
read_warn_details (name, offset, size);
|
||||
}
|
||||
else
|
||||
@@ -1094,7 +1149,7 @@ readlink_diag (char const *name)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
readlink_warn (name);
|
||||
}
|
||||
else
|
||||
@@ -1106,7 +1161,7 @@ savedir_diag (char const *name)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
savedir_warn (name);
|
||||
}
|
||||
else
|
||||
@@ -1118,7 +1173,7 @@ seek_diag_details (char const *name, off_t offset)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
seek_warn_details (name, offset);
|
||||
}
|
||||
else
|
||||
@@ -1130,7 +1185,7 @@ stat_diag (char const *name)
|
||||
{
|
||||
if (ignore_failed_read_option)
|
||||
{
|
||||
if (WARNING_ENABLED(WARN_FAILED_READ))
|
||||
if (warning_enabled (WARN_FAILED_READ))
|
||||
stat_warn (name);
|
||||
}
|
||||
else
|
||||
@@ -1143,9 +1198,8 @@ file_removed_diag (const char *name, bool top_level,
|
||||
{
|
||||
if (!top_level && errno == ENOENT)
|
||||
{
|
||||
WARNOPT (WARN_FILE_REMOVED,
|
||||
(0, 0, _("%s: File removed before we read it"),
|
||||
quotearg_colon (name)));
|
||||
warnopt (WARN_FILE_REMOVED, 0, _("%s: File removed before we read it"),
|
||||
quotearg_colon (name));
|
||||
set_exit_status (TAREXIT_DIFFERS);
|
||||
}
|
||||
else
|
||||
@@ -1157,7 +1211,7 @@ pid_t
|
||||
xfork (void)
|
||||
{
|
||||
pid_t p = fork ();
|
||||
if (p == (pid_t) -1)
|
||||
if (p < 0)
|
||||
call_arg_fatal ("fork", _("child process"));
|
||||
return p;
|
||||
}
|
||||
@@ -1170,40 +1224,13 @@ xpipe (int fd[2])
|
||||
call_arg_fatal ("pipe", _("interprocess channel"));
|
||||
}
|
||||
|
||||
/* Return PTR, aligned upward to the next multiple of ALIGNMENT.
|
||||
ALIGNMENT must be nonzero. The caller must arrange for ((char *)
|
||||
PTR) through ((char *) PTR + ALIGNMENT - 1) to be addressable
|
||||
locations. */
|
||||
|
||||
static inline void *
|
||||
ptr_align (void *ptr, size_t alignment)
|
||||
{
|
||||
char *p0 = ptr;
|
||||
char *p1 = p0 + alignment - 1;
|
||||
return p1 - (size_t) p1 % alignment;
|
||||
}
|
||||
|
||||
/* Return the address of a page-aligned buffer of at least SIZE bytes.
|
||||
The caller should free *PTR when done with the buffer. */
|
||||
|
||||
void *
|
||||
page_aligned_alloc (void **ptr, size_t size)
|
||||
{
|
||||
size_t alignment = getpagesize ();
|
||||
size_t size1 = size + alignment;
|
||||
if (size1 < size)
|
||||
xalloc_die ();
|
||||
*ptr = xmalloc (size1);
|
||||
return ptr_align (*ptr, alignment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct namebuf
|
||||
{
|
||||
char *buffer; /* directory, '/', and directory member */
|
||||
size_t buffer_size; /* allocated size of name_buffer */
|
||||
size_t dir_length; /* length of directory part in buffer */
|
||||
idx_t buffer_size; /* allocated size of name_buffer */
|
||||
idx_t dir_length; /* length of directory part in buffer */
|
||||
};
|
||||
|
||||
namebuf_t
|
||||
@@ -1229,9 +1256,10 @@ namebuf_free (namebuf_t buf)
|
||||
char *
|
||||
namebuf_name (namebuf_t buf, const char *name)
|
||||
{
|
||||
size_t len = strlen (name);
|
||||
while (buf->dir_length + len + 1 >= buf->buffer_size)
|
||||
buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
|
||||
idx_t len = strlen (name);
|
||||
ptrdiff_t incr_min = buf->dir_length + len + 2 - buf->buffer_size;
|
||||
if (0 < incr_min)
|
||||
buf->buffer = xpalloc (buf->buffer, &buf->buffer_size, incr_min, -1, 1);
|
||||
strcpy (buf->buffer + buf->dir_length, name);
|
||||
return buf->buffer;
|
||||
}
|
||||
@@ -1267,7 +1295,7 @@ namebuf_finish (namebuf_t buf)
|
||||
Return NULL on errors.
|
||||
*/
|
||||
char *
|
||||
tar_savedir (const char *name, int must_exist)
|
||||
tar_savedir (const char *name, bool must_exist)
|
||||
{
|
||||
char *ret = NULL;
|
||||
DIR *dir = NULL;
|
||||
@@ -1282,7 +1310,7 @@ tar_savedir (const char *name, int must_exist)
|
||||
&& (ret = streamsavedir (dir, savedir_sort_order))))
|
||||
savedir_error (name);
|
||||
|
||||
if (dir ? closedir (dir) != 0 : 0 <= fd && close (fd) != 0)
|
||||
if (dir ? closedir (dir) < 0 : 0 <= fd && close (fd) < 0)
|
||||
savedir_error (name);
|
||||
|
||||
return ret;
|
||||
|
||||
692
src/names.c
692
src/names.c
File diff suppressed because it is too large
Load Diff
522
src/sparse.c
522
src/sparse.c
@@ -1,6 +1,6 @@
|
||||
/* Functions for dealing with sparse files
|
||||
|
||||
Copyright 2003-2019 Free Software Foundation, Inc.
|
||||
Copyright 2003-2025 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)
|
||||
@@ -335,8 +340,6 @@ sparse_scan_file_seek (struct tar_sparse_file *file)
|
||||
st->archive_file_size += sp.numbytes;
|
||||
offset = hole_offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -356,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
|
||||
@@ -403,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))
|
||||
@@ -413,56 +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;
|
||||
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;
|
||||
|
||||
blk = find_next_block ();
|
||||
bytes_read = safe_read (file->fd, blk->buffer, bufsize);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
if (bytes_read < bufsize)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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
|
||||
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)));
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
|
||||
bytes_left -= bytes_read;
|
||||
file->dumped_size += bytes_read;
|
||||
set_next_block_after (blk);
|
||||
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;
|
||||
|
||||
@@ -479,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;
|
||||
@@ -526,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);
|
||||
}
|
||||
}
|
||||
@@ -567,10 +560,12 @@ 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))
|
||||
return dump_status_not_implemented;
|
||||
{
|
||||
*size = st->stat.st_size;
|
||||
return dump_status_not_implemented;
|
||||
}
|
||||
|
||||
file.stat_info = st;
|
||||
file.fd = fd;
|
||||
@@ -578,14 +573,14 @@ 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;
|
||||
}
|
||||
|
||||
enum dump_status
|
||||
sparse_skip_file (struct tar_stat_info *st)
|
||||
sparse_skim_file (struct tar_stat_info *st, bool must_copy)
|
||||
{
|
||||
bool rc = true;
|
||||
struct tar_sparse_file file;
|
||||
@@ -597,7 +592,7 @@ sparse_skip_file (struct tar_stat_info *st)
|
||||
file.fd = -1;
|
||||
|
||||
rc = tar_sparse_decode_header (&file);
|
||||
skip_file (file.stat_info->archive_file_size - file.dumped_size);
|
||||
skim_file (file.stat_info->archive_file_size - file.dumped_size, must_copy);
|
||||
return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
|
||||
}
|
||||
|
||||
@@ -610,24 +605,19 @@ 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 = safe_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);
|
||||
if (errno)
|
||||
read_diag_details (file->stat_info->orig_file_name, beg, rdsize);
|
||||
else
|
||||
report_difference (file->stat_info, _("Size differs"));
|
||||
return false;
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
{
|
||||
report_difference (file->stat_info, _("Size differs"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!zero_block_p (diff_buffer, bytes_read))
|
||||
{
|
||||
char begbuf[INT_BUFSIZE_BOUND (off_t)];
|
||||
@@ -644,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;
|
||||
|
||||
@@ -655,40 +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 = safe_read (file->fd, diff_buffer, rdsize);
|
||||
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
|
||||
- size_left),
|
||||
rdsize);
|
||||
return false;
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
{
|
||||
report_difference (¤t_stat_info, _("Size differs"));
|
||||
return false;
|
||||
}
|
||||
file->dumped_size += BLOCKSIZE;
|
||||
idx_t rdsize = min (size_left, BLOCKSIZE);
|
||||
idx_t bytes_read = full_read (file->fd, diff_buffer, rdsize);
|
||||
size_left -= bytes_read;
|
||||
mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
|
||||
if (memcmp (blk->buffer, diff_buffer, rdsize))
|
||||
if (memcmp (blk->buffer, diff_buffer, bytes_read) != 0)
|
||||
{
|
||||
report_difference (file->stat_info, _("Contents differ"));
|
||||
return false;
|
||||
}
|
||||
|
||||
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 - bytes_read);
|
||||
else
|
||||
report_difference (¤t_stat_info, _("Size differs"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -698,11 +686,10 @@ 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))
|
||||
return dump_status_not_implemented;
|
||||
return false;
|
||||
|
||||
file.stat_info = st;
|
||||
file.fd = fd;
|
||||
@@ -710,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)
|
||||
@@ -720,7 +707,7 @@ sparse_diff_file (int fd, struct tar_stat_info *st)
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
skip_file (file.stat_info->archive_file_size - file.dumped_size);
|
||||
skim_file (file.stat_info->archive_file_size - file.dumped_size, false);
|
||||
mv_end ();
|
||||
|
||||
tar_sparse_done (&file);
|
||||
@@ -754,7 +741,7 @@ enum oldgnu_add_status
|
||||
};
|
||||
|
||||
static bool
|
||||
oldgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
|
||||
oldgnu_sparse_member_p (MAYBE_UNUSED struct tar_sparse_file *file)
|
||||
{
|
||||
return current_header->header.typeflag == GNUTYPE_SPARSE;
|
||||
}
|
||||
@@ -769,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;
|
||||
|
||||
@@ -794,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)
|
||||
@@ -849,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;
|
||||
@@ -861,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);
|
||||
@@ -898,7 +883,7 @@ static struct tar_sparse_optab const oldgnu_optab = {
|
||||
/* Star */
|
||||
|
||||
static bool
|
||||
star_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
|
||||
star_sparse_member_p (MAYBE_UNUSED struct tar_sparse_file *file)
|
||||
{
|
||||
return current_header->header.typeflag == GNUTYPE_SPARSE;
|
||||
}
|
||||
@@ -918,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;
|
||||
@@ -929,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)
|
||||
@@ -945,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;
|
||||
@@ -1018,7 +1002,7 @@ static struct tar_sparse_optab const star_optab = {
|
||||
* 1.0
|
||||
|
||||
Starting from this version, the exact sparse format version is specified
|
||||
explicitely in the header using the following variables:
|
||||
explicitly in the header using the following variables:
|
||||
|
||||
GNU.sparse.major Major version
|
||||
GNU.sparse.minor Minor version
|
||||
@@ -1077,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;
|
||||
@@ -1089,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);
|
||||
@@ -1103,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, ",");
|
||||
@@ -1131,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);
|
||||
@@ -1186,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;
|
||||
}
|
||||
|
||||
@@ -1220,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
|
||||
@@ -1244,80 +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)))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
88
src/suffix.c
88
src/suffix.c
@@ -1,5 +1,5 @@
|
||||
/* This file is part of GNU tar.
|
||||
Copyright 2007-2019 Free Software Foundation, Inc.
|
||||
Copyright 2007-2025 Free Software Foundation, Inc.
|
||||
|
||||
Written by Sergey Poznyakoff.
|
||||
|
||||
@@ -21,16 +21,19 @@
|
||||
|
||||
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 +46,72 @@ 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
|
||||
+ sizeof compression_suffixes / sizeof *compression_suffixes);
|
||||
p++)
|
||||
if (strcmp (p->suffix, suf) == 0)
|
||||
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)
|
||||
|
||||
531
src/system.c
531
src/system.c
@@ -1,6 +1,6 @@
|
||||
/* System-dependent calls for tar.
|
||||
|
||||
Copyright 2003-2019 Free Software Foundation, Inc.
|
||||
Copyright 2003-2025 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
|
||||
@@ -17,11 +17,21 @@
|
||||
|
||||
#include <system.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)
|
||||
@@ -37,7 +47,36 @@ xexec (const char *cmd)
|
||||
exec_fatal (cmd);
|
||||
}
|
||||
|
||||
#if MSDOS
|
||||
/* True if the archive is seekable via ioctl and MTIOCTOP,
|
||||
or if it is not known whether it is seekable.
|
||||
False if it is known to be not seekable. */
|
||||
static bool mtioseekable_archive;
|
||||
|
||||
bool
|
||||
mtioseek (bool count_files, off_t count)
|
||||
{
|
||||
if (mtioseekable_archive)
|
||||
{
|
||||
#ifdef MTIOCTOP
|
||||
struct mtop operation;
|
||||
operation.mt_op = (count_files
|
||||
? (count < 0 ? MTBSF : MTFSF)
|
||||
: (count < 0 ? MTBSR : MTFSR));
|
||||
if (! (count < 0
|
||||
? ckd_sub (&operation.mt_count, 0, count)
|
||||
: ckd_add (&operation.mt_count, count, 0))
|
||||
&& (0 <= rmtioctl (archive, MTIOCTOP, &operation)
|
||||
|| (errno == EIO
|
||||
&& 0 <= rmtioctl (archive, MTIOCTOP, &operation))))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
mtioseekable_archive = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !HAVE_WAITPID /* MingW, MSVC 14. */
|
||||
|
||||
bool
|
||||
sys_get_archive_stat (void)
|
||||
@@ -51,11 +90,6 @@ sys_file_is_archive (struct tar_stat_info *p)
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
sys_save_archive_dev_ino (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
sys_detect_dev_null_output (void)
|
||||
{
|
||||
@@ -92,82 +126,86 @@ 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 */
|
||||
|
||||
static struct stat archive_stat; /* stat block for archive file */
|
||||
|
||||
bool
|
||||
sys_get_archive_stat (void)
|
||||
{
|
||||
return fstat (archive, &archive_stat) == 0;
|
||||
bool remote = _isrmt (archive);
|
||||
mtioseekable_archive = true;
|
||||
if (!remote && 0 <= archive && fstat (archive, &archive_stat) == 0)
|
||||
{
|
||||
if (!S_ISCHR (archive_stat.st_mode))
|
||||
mtioseekable_archive = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FIXME: This memset should not be needed. It is present only
|
||||
because other parts of tar may incorrectly access
|
||||
archive_stat even if it's not the archive status. */
|
||||
memset (&archive_stat, 0, sizeof archive_stat);
|
||||
|
||||
return remote;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sys_file_is_archive (struct tar_stat_info *p)
|
||||
{
|
||||
return (ar_dev && p->stat.st_dev == ar_dev && p->stat.st_ino == ar_ino);
|
||||
return (!dev_null_output && !_isrmt (archive)
|
||||
&& psame_inode (&p->stat, &archive_stat));
|
||||
}
|
||||
|
||||
/* Save archive file inode and device numbers */
|
||||
void
|
||||
sys_save_archive_dev_ino (void)
|
||||
{
|
||||
if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode))
|
||||
{
|
||||
ar_dev = archive_stat.st_dev;
|
||||
ar_ino = archive_stat.st_ino;
|
||||
}
|
||||
else
|
||||
ar_dev = 0;
|
||||
}
|
||||
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";
|
||||
struct stat dev_null_stat;
|
||||
static struct stat dev_null_stat;
|
||||
|
||||
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|
||||
|| (! _isrmt (archive)
|
||||
&& S_ISCHR (archive_stat.st_mode)
|
||||
&& stat (dev_null, &dev_null_stat) == 0
|
||||
&& archive_stat.st_dev == dev_null_stat.st_dev
|
||||
&& archive_stat.st_ino == dev_null_stat.st_ino));
|
||||
&& (dev_null_stat.st_ino != 0
|
||||
|| stat (dev_null, &dev_null_stat) == 0)
|
||||
&& psame_inode (&archive_stat, &dev_null_stat)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -177,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);
|
||||
@@ -188,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,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);
|
||||
@@ -234,41 +272,39 @@ 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
|
||||
|
||||
/* Duplicate file descriptor FROM into becoming INTO.
|
||||
INTO is closed first and has to be the next available slot. */
|
||||
@@ -277,37 +313,20 @@ xdup2 (int from, int into)
|
||||
{
|
||||
if (from != into)
|
||||
{
|
||||
int status = close (into);
|
||||
|
||||
if (status != 0 && errno != EBADF)
|
||||
{
|
||||
int e = errno;
|
||||
FATAL_ERROR ((0, e, _("Cannot close")));
|
||||
}
|
||||
status = dup (from);
|
||||
if (status != into)
|
||||
{
|
||||
if (status < 0)
|
||||
{
|
||||
int e = errno;
|
||||
FATAL_ERROR ((0, e, _("Cannot dup")));
|
||||
}
|
||||
abort ();
|
||||
}
|
||||
if (dup2 (from, into) < 0)
|
||||
paxfatal (errno, _("Cannot dup2"));
|
||||
xclose (from);
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
|
||||
|
||||
/* Propagate any failure of the grandchild back to the parent. */
|
||||
static void
|
||||
static _Noreturn void
|
||||
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);
|
||||
@@ -419,20 +438,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;
|
||||
@@ -448,7 +467,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);
|
||||
@@ -481,24 +500,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);
|
||||
}
|
||||
|
||||
@@ -540,7 +557,7 @@ sys_child_open_for_uncompress (void)
|
||||
&& !_remdev (archive_name_array[0])
|
||||
&& is_regular_file (archive_name_array[0]))
|
||||
{
|
||||
/* We don't need a grandchild tar. Open the archive and lauch the
|
||||
/* We don't need a grandchild tar. Open the archive and launch the
|
||||
uncompressor. */
|
||||
|
||||
archive = open (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW);
|
||||
@@ -586,34 +603,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);
|
||||
@@ -626,11 +635,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 ();
|
||||
}
|
||||
|
||||
@@ -638,17 +644,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 ();
|
||||
}
|
||||
|
||||
@@ -657,7 +665,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
|
||||
@@ -670,7 +678,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 ();
|
||||
}
|
||||
|
||||
@@ -725,7 +733,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];
|
||||
|
||||
@@ -758,7 +766,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;
|
||||
@@ -769,53 +777,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);
|
||||
@@ -824,31 +844,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]);
|
||||
|
||||
@@ -859,12 +867,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)
|
||||
{
|
||||
@@ -872,7 +877,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);
|
||||
@@ -883,17 +888,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 */
|
||||
|
||||
139
src/tar.h
139
src/tar.h
@@ -1,6 +1,6 @@
|
||||
/* GNU tar Archive Format description.
|
||||
|
||||
Copyright 1988-2019 Free Software Foundation, Inc.
|
||||
Copyright 1988-2025 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,9 +298,16 @@ struct xattr_array
|
||||
{
|
||||
char *xkey;
|
||||
char *xval_ptr;
|
||||
size_t xval_len;
|
||||
idx_t xval_len;
|
||||
};
|
||||
|
||||
struct xattr_map
|
||||
{
|
||||
struct xattr_array *xm_map;
|
||||
idx_t xm_size; /* Size of the xattr map */
|
||||
idx_t xm_max; /* Max. number of entries in xattr_map */
|
||||
};
|
||||
|
||||
struct tar_stat_info
|
||||
{
|
||||
char *orig_file_name; /* name of file read from the archive header */
|
||||
@@ -299,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 */
|
||||
|
||||
@@ -318,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 */
|
||||
@@ -334,8 +358,7 @@ struct tar_stat_info
|
||||
processed pax header parsing. Following 'path'
|
||||
header (lower priority) will be ignored. */
|
||||
|
||||
size_t xattr_map_size; /* Size of the xattr map */
|
||||
struct xattr_array *xattr_map;
|
||||
struct xattr_map xattr_map;
|
||||
|
||||
/* Extended headers */
|
||||
struct xheader xhdr;
|
||||
|
||||
266
src/transform.c
266
src/transform.c
@@ -1,5 +1,5 @@
|
||||
/* This file is part of GNU tar.
|
||||
Copyright 2006-2019 Free Software Foundation, Inc.
|
||||
Copyright 2006-2025 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,16 +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)
|
||||
paxusage (_("Invalid transform expression"));
|
||||
|
||||
/* Scan regular expression */
|
||||
for (i = 2; expr[i] && expr[i] != delim; i++)
|
||||
@@ -215,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++)
|
||||
@@ -223,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;
|
||||
@@ -245,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 == ';')
|
||||
@@ -263,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] == '$'))
|
||||
@@ -287,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 '\\':
|
||||
@@ -414,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);
|
||||
|
||||
@@ -491,9 +478,6 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
|
||||
disp = rmp[0].rm_eo;
|
||||
|
||||
if (rmp[0].rm_so)
|
||||
obstack_grow (&stk, input, rmp[0].rm_so);
|
||||
|
||||
nmatches++;
|
||||
if (tf->match_number && nmatches < tf->match_number)
|
||||
{
|
||||
@@ -502,37 +486,36 @@ _single_transform_name_to_obstack (struct transform *tf, char *input)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rmp[0].rm_so)
|
||||
obstack_grow (&stk, input, rmp[0].rm_so);
|
||||
|
||||
for (segm = tf->repl_head; segm; segm = segm->next)
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -579,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)
|
||||
{
|
||||
@@ -597,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
|
||||
|
||||
65
src/unlink.c
65
src/unlink.c
@@ -1,6 +1,6 @@
|
||||
/* Unlink files.
|
||||
|
||||
Copyright 2009-2019 Free Software Foundation, Inc.
|
||||
Copyright 2009-2025 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,7 @@ flush_deferred_unlinks (bool force)
|
||||
else
|
||||
fname = p->file_name;
|
||||
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
@@ -143,11 +132,10 @@ flush_deferred_unlinks (bool force)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unlinkat (chdir_fd, p->file_name, 0) != 0 && errno != ENOENT)
|
||||
if (unlinkat (chdir_fd, p->file_name, 0) < 0 && errno != ENOENT)
|
||||
unlink_error (p->file_name);
|
||||
}
|
||||
dunlink_reclaim (p);
|
||||
dunlink_count--;
|
||||
p = next;
|
||||
if (prev)
|
||||
prev->next = p;
|
||||
@@ -170,7 +158,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 +166,17 @@ flush_deferred_unlinks (bool force)
|
||||
else
|
||||
fname = p->file_name;
|
||||
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) != 0)
|
||||
if (unlinkat (chdir_fd, fname, AT_REMOVEDIR) < 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
rmdir_error (fname);
|
||||
}
|
||||
dunlink_reclaim (p);
|
||||
dunlink_count--;
|
||||
p = next;
|
||||
}
|
||||
dunlink_head = dunlink_tail = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
chdir_do (saved_chdir);
|
||||
}
|
||||
|
||||
@@ -197,7 +184,7 @@ void
|
||||
finish_deferred_unlinks (void)
|
||||
{
|
||||
flush_deferred_unlinks (true);
|
||||
|
||||
|
||||
while (dunlink_avail)
|
||||
{
|
||||
struct deferred_unlink *next = dunlink_avail->next;
|
||||
@@ -212,7 +199,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 +210,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);
|
||||
|
||||
106
src/update.c
106
src/update.c
@@ -1,6 +1,6 @@
|
||||
/* Update a tar archive.
|
||||
|
||||
Copyright 1988-2019 Free Software Foundation, Inc.
|
||||
Copyright 1988-2025 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).
|
||||
@@ -42,13 +38,14 @@ bool time_to_start_writing;
|
||||
first part of the record. */
|
||||
char *output_start;
|
||||
|
||||
static bool acting_as_filter;
|
||||
|
||||
/* Catenate file FILE_NAME to the archive without creating a header for it.
|
||||
It had better be a tar file or the archive is screwed. */
|
||||
static void
|
||||
append_file (char *file_name)
|
||||
{
|
||||
int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
|
||||
struct stat stat_data;
|
||||
|
||||
if (handle < 0)
|
||||
{
|
||||
@@ -56,49 +53,44 @@ append_file (char *file_name)
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat (handle, &stat_data) != 0)
|
||||
stat_error (file_name);
|
||||
else
|
||||
while (true)
|
||||
{
|
||||
off_t bytes_left = stat_data.st_size;
|
||||
|
||||
while (bytes_left > 0)
|
||||
{
|
||||
union block *start = find_next_block ();
|
||||
size_t buffer_size = available_space_after (start);
|
||||
size_t status;
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
|
||||
if (bytes_left < buffer_size)
|
||||
{
|
||||
buffer_size = bytes_left;
|
||||
status = buffer_size % BLOCKSIZE;
|
||||
if (status)
|
||||
memset (start->buffer + bytes_left, 0, BLOCKSIZE - status);
|
||||
}
|
||||
|
||||
status = safe_read (handle, start->buffer, buffer_size);
|
||||
if (status == SAFE_READ_ERROR)
|
||||
read_fatal_details (file_name, stat_data.st_size - bytes_left,
|
||||
buffer_size);
|
||||
if (status == 0)
|
||||
FATAL_ERROR ((0, 0,
|
||||
ngettext ("%s: File shrank by %s byte",
|
||||
"%s: File shrank by %s bytes",
|
||||
bytes_left),
|
||||
quotearg_colon (file_name),
|
||||
STRINGIFY_BIGINT (bytes_left, buf)));
|
||||
|
||||
bytes_left -= status;
|
||||
|
||||
set_next_block_after (start + (status - 1) / BLOCKSIZE);
|
||||
}
|
||||
union block *start = find_next_block ();
|
||||
idx_t bufsize = available_space_after (start);
|
||||
idx_t status = full_read (handle, charptr (start), bufsize);
|
||||
if (status < bufsize && errno)
|
||||
read_fatal (file_name);
|
||||
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);
|
||||
}
|
||||
|
||||
/* If NAME is not a pattern, remove it from the namelist. Otherwise,
|
||||
remove the FILE_NAME that matched it. Take care to look for exact
|
||||
match when removing it. */
|
||||
static void
|
||||
remove_exact_name (struct name *name, char const *file_name)
|
||||
{
|
||||
if (name->is_wildcard)
|
||||
{
|
||||
struct name *match = name_scan (file_name, true);
|
||||
name->found_count++;
|
||||
if (match)
|
||||
name = match;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
remname (name);
|
||||
}
|
||||
|
||||
/* Implement the 'r' (add files to end of archive), and 'u' (add files
|
||||
to end of archive if they aren't there, or are more up to date than
|
||||
the version in the archive) commands. */
|
||||
@@ -110,6 +102,7 @@ update_archive (void)
|
||||
|
||||
name_gather ();
|
||||
open_archive (ACCESS_UPDATE);
|
||||
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
|
||||
xheader_forbid_global ();
|
||||
|
||||
while (!found_end)
|
||||
@@ -129,13 +122,13 @@ 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;
|
||||
|
||||
if (subcommand_option == UPDATE_SUBCOMMAND
|
||||
&& (name = name_scan (current_stat_info.file_name)) != NULL)
|
||||
&& (name = name_scan (current_stat_info.file_name, false)) != NULL)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
@@ -144,10 +137,12 @@ update_archive (void)
|
||||
{
|
||||
if (S_ISDIR (s.st_mode))
|
||||
{
|
||||
char *p, *dirp = tar_savedir (name->name, 1);
|
||||
char *p;
|
||||
char *dirp = tar_savedir (current_stat_info.file_name,
|
||||
true);
|
||||
if (dirp)
|
||||
{
|
||||
namebuf_t nbuf = namebuf_create (name->name);
|
||||
namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
|
||||
|
||||
for (p = dirp; *p; p += strlen (p) + 1)
|
||||
addname (namebuf_name (nbuf, p),
|
||||
@@ -156,17 +151,22 @@ update_archive (void)
|
||||
namebuf_free (nbuf);
|
||||
free (dirp);
|
||||
|
||||
remname (name);
|
||||
remove_exact_name (name, current_stat_info.file_name);
|
||||
}
|
||||
}
|
||||
else if (tar_timespec_cmp (get_stat_mtime (&s),
|
||||
current_stat_info.mtime)
|
||||
<= 0)
|
||||
remname (name);
|
||||
{
|
||||
remove_exact_name (name, current_stat_info.file_name);
|
||||
}
|
||||
else if (name->is_wildcard)
|
||||
addname (current_stat_info.file_name,
|
||||
name->change_dir, false, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
skip_member ();
|
||||
skim_member (acting_as_filter);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -184,11 +184,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;
|
||||
@@ -206,7 +206,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;
|
||||
|
||||
22
src/utf8.c
22
src/utf8.c
@@ -1,6 +1,6 @@
|
||||
/* Charset handling for GNU tar.
|
||||
|
||||
Copyright 2004-2019 Free Software Foundation, Inc.
|
||||
Copyright 2004-2025 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
|
||||
@@ -77,11 +78,14 @@ utf8_convert (bool to_utf, char const *input, char **output)
|
||||
*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-2019 Free Software Foundation, Inc.
|
||||
Copyright 2009-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -48,6 +48,9 @@ static char const *const warning_args[] = {
|
||||
"xattr-write",
|
||||
"record-size",
|
||||
"failed-read",
|
||||
"missing-zero-blocks",
|
||||
"verbose",
|
||||
"empty-transform",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -76,17 +79,20 @@ static int warning_types[] = {
|
||||
WARN_EXISTING_FILE,
|
||||
WARN_XATTR_WRITE,
|
||||
WARN_RECORD_SIZE,
|
||||
WARN_FAILED_READ
|
||||
WARN_FAILED_READ,
|
||||
WARN_MISSING_ZERO_BLOCKS,
|
||||
WARN_VERBOSE_WARNINGS,
|
||||
WARN_EMPTY_TRANSFORM
|
||||
};
|
||||
|
||||
ARGMATCH_VERIFY (warning_args, warning_types);
|
||||
|
||||
int warning_option = WARN_ALL;
|
||||
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)
|
||||
@@ -96,7 +102,7 @@ set_warning_option (const char *arg)
|
||||
}
|
||||
if (strlen (arg) > 2 && memcmp (arg, "no-", 3) == 0)
|
||||
{
|
||||
negate = 1;
|
||||
negate = true;
|
||||
arg += 3;
|
||||
}
|
||||
|
||||
@@ -107,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);
|
||||
}
|
||||
}
|
||||
|
||||
404
src/xattrs.c
404
src/xattrs.c
@@ -1,6 +1,6 @@
|
||||
/* Support for extended attributes.
|
||||
|
||||
Copyright (C) 2006-2019 Free Software Foundation, Inc.
|
||||
Copyright (C) 2006-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU tar.
|
||||
|
||||
@@ -30,11 +30,82 @@
|
||||
#include "xattr-at.h"
|
||||
#include "selinux-at.h"
|
||||
|
||||
static char const XATTRS_PREFIX[] = "SCHILY.xattr.";
|
||||
enum { XATTRS_PREFIX_LEN = sizeof XATTRS_PREFIX - 1 };
|
||||
|
||||
void
|
||||
xheader_xattr_init (struct tar_stat_info *st)
|
||||
{
|
||||
xattr_map_init (&st->xattr_map);
|
||||
|
||||
st->acls_a_ptr = NULL;
|
||||
st->acls_a_len = 0;
|
||||
st->acls_d_ptr = NULL;
|
||||
st->acls_d_len = 0;
|
||||
st->cntx_name = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
xattr_map_init (struct xattr_map *map)
|
||||
{
|
||||
memset (map, 0, sizeof *map);
|
||||
}
|
||||
|
||||
void
|
||||
xattr_map_free (struct xattr_map *xattr_map)
|
||||
{
|
||||
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);
|
||||
}
|
||||
free (xattr_map->xm_map);
|
||||
}
|
||||
|
||||
void
|
||||
xattr_map_add (struct xattr_map *map,
|
||||
const char *key, const char *val, idx_t len)
|
||||
{
|
||||
if (map->xm_size == map->xm_max)
|
||||
map->xm_map = xpalloc (map->xm_map, &map->xm_max, 1, -1,
|
||||
sizeof *map->xm_map);
|
||||
struct xattr_array *p = &map->xm_map[map->xm_size];
|
||||
p->xkey = xstrdup (key);
|
||||
p->xval_ptr = ximemdup (val, len + 1);
|
||||
p->xval_len = len;
|
||||
map->xm_size++;
|
||||
}
|
||||
|
||||
static void
|
||||
xheader_xattr_add (struct tar_stat_info *st,
|
||||
const char *key, const char *val, idx_t len)
|
||||
{
|
||||
idx_t klen = strlen (key);
|
||||
char *xkey = xmalloc (XATTRS_PREFIX_LEN + klen + 1);
|
||||
char *tmp = xkey;
|
||||
|
||||
tmp = stpcpy (tmp, XATTRS_PREFIX);
|
||||
stpcpy (tmp, key);
|
||||
|
||||
xattr_map_add (&st->xattr_map, xkey, val, len);
|
||||
|
||||
free (xkey);
|
||||
}
|
||||
|
||||
void
|
||||
xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
struct xattrs_mask_map
|
||||
{
|
||||
const char **masks;
|
||||
size_t size;
|
||||
size_t used;
|
||||
idx_t size;
|
||||
idx_t used;
|
||||
};
|
||||
|
||||
/* list of fnmatch patterns */
|
||||
@@ -53,6 +124,10 @@ static struct
|
||||
#ifdef HAVE_POSIX_ACLS
|
||||
# include "acl.h"
|
||||
# include <sys/acl.h>
|
||||
# ifdef HAVE_ACL_LIBACL_H
|
||||
# /* needed for numeric-owner support */
|
||||
# include <acl/libacl.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_ACLS
|
||||
@@ -179,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;
|
||||
@@ -203,14 +278,13 @@ 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 *ptr, size_t len, bool def)
|
||||
char const *file_name, acl_type_t type,
|
||||
char *ptr, bool def)
|
||||
{
|
||||
acl_t acl;
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
/* assert (strlen (ptr) == len); */
|
||||
ptr = fixup_extra_acl_fields (ptr);
|
||||
acl = acl_from_text (ptr);
|
||||
}
|
||||
@@ -219,12 +293,11 @@ 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,
|
||||
if (acl_delete_def_file_at (chdir_fd, file_name) < 0)
|
||||
warnopt (WARN_XATTR_WRITE, errno,
|
||||
_("acl_delete_def_file_at: Cannot drop default POSIX ACLs "
|
||||
"for file '%s'"),
|
||||
file_name));
|
||||
file_name);
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -236,12 +309,11 @@ xattrs__acls_set (struct tar_stat_info const *st,
|
||||
return;
|
||||
}
|
||||
|
||||
if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
|
||||
if (acl_set_file_at (chdir_fd, file_name, 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'"),
|
||||
file_name);
|
||||
|
||||
acl_free (acl);
|
||||
}
|
||||
@@ -249,7 +321,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;
|
||||
|
||||
@@ -271,21 +343,36 @@ xattrs_acls_cleanup (char *val, size_t *plen)
|
||||
}
|
||||
|
||||
static void
|
||||
xattrs__acls_get_a (int parentfd, const char *file_name,
|
||||
struct tar_stat_info *st,
|
||||
char **ret_ptr, size_t * ret_len)
|
||||
acls_get_text (int parentfd, const char *file_name, acl_type_t type,
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
char *val = NULL;
|
||||
acl_t acl;
|
||||
|
||||
if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS)))
|
||||
if (!(acl = acl_get_file_at (parentfd, file_name, type)))
|
||||
{
|
||||
if (errno != ENOTSUP)
|
||||
call_arg_warn ("acl_get_file_at", file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
val = acl_to_text (acl, NULL);
|
||||
if (numeric_owner_option)
|
||||
{
|
||||
#ifdef HAVE_ACL_LIBACL_H
|
||||
val = acl_to_any_text (acl, NULL, '\n',
|
||||
TEXT_SOME_EFFECTIVE | TEXT_NUMERIC_IDS);
|
||||
#else
|
||||
static bool warned;
|
||||
if (!warned)
|
||||
{
|
||||
warned = true;
|
||||
paxwarn (0, _("--numeric-owner is ignored for ACLs:"
|
||||
" libacl is not available"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
val = acl_to_text (acl, NULL);
|
||||
acl_free (acl);
|
||||
|
||||
if (!val)
|
||||
@@ -299,46 +386,31 @@ xattrs__acls_get_a (int parentfd, const char *file_name,
|
||||
acl_free (val);
|
||||
}
|
||||
|
||||
static void
|
||||
xattrs__acls_get_a (int parentfd, const char *file_name,
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
acls_get_text (parentfd, file_name, ACL_TYPE_ACCESS, ret_ptr, ret_len);
|
||||
}
|
||||
|
||||
/* "system.posix_acl_default" */
|
||||
static void
|
||||
xattrs__acls_get_d (int parentfd, char const *file_name,
|
||||
struct tar_stat_info *st,
|
||||
char **ret_ptr, size_t * ret_len)
|
||||
char **ret_ptr, idx_t *ret_len)
|
||||
{
|
||||
char *val = NULL;
|
||||
acl_t acl;
|
||||
|
||||
if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT)))
|
||||
{
|
||||
if (errno != ENOTSUP)
|
||||
call_arg_warn ("acl_get_file_at", file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
val = acl_to_text (acl, NULL);
|
||||
acl_free (acl);
|
||||
|
||||
if (!val)
|
||||
{
|
||||
call_arg_warn ("acl_to_text", file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
*ret_ptr = xstrdup (val);
|
||||
xattrs_acls_cleanup (*ret_ptr, ret_len);
|
||||
acl_free (val);
|
||||
acls_get_text (parentfd, file_name, ACL_TYPE_DEFAULT, ret_ptr, ret_len);
|
||||
}
|
||||
#endif /* HAVE_POSIX_ACLS */
|
||||
|
||||
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;
|
||||
@@ -346,7 +418,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;
|
||||
|
||||
@@ -362,58 +434,63 @@ 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 fd, 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;
|
||||
}
|
||||
|
||||
xattrs__acls_get_a (parentfd, file_name, st,
|
||||
xattrs__acls_get_a (parentfd, file_name,
|
||||
&st->acls_a_ptr, &st->acls_a_len);
|
||||
if (!xisfile)
|
||||
xattrs__acls_get_d (parentfd, file_name, st,
|
||||
xattrs__acls_get_d (parentfd, file_name,
|
||||
&st->acls_d_ptr, &st->acls_d_len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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, st->acls_a_len, false);
|
||||
st->acls_a_ptr, false);
|
||||
if (typeflag == DIRTYPE || typeflag == GNUTYPE_DUMPDIR)
|
||||
xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
|
||||
st->acls_d_ptr, st->acls_d_len, true);
|
||||
st->acls_d_ptr, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -422,11 +499,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
|
||||
@@ -441,20 +514,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)
|
||||
@@ -465,56 +524,55 @@ void
|
||||
xattrs_xattrs_get (int parentfd, char const *file_name,
|
||||
struct tar_stat_info *st, 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);
|
||||
@@ -533,14 +591,13 @@ xattrs_xattrs_get (int parentfd, char const *file_name,
|
||||
|
||||
#ifdef HAVE_XATTRS
|
||||
static void
|
||||
xattrs__fd_set (struct tar_stat_info const *st,
|
||||
char const *file_name, char typeflag,
|
||||
const char *attr, const char *ptr, size_t len)
|
||||
xattrs__fd_set (char const *file_name, char typeflag,
|
||||
const char *attr, const char *ptr, idx_t len)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
const char *sysname = "setxattrat";
|
||||
int ret = -1;
|
||||
int ret;
|
||||
|
||||
if (typeflag != SYMTYPE)
|
||||
ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
|
||||
@@ -550,11 +607,10 @@ xattrs__fd_set (struct tar_stat_info const *st,
|
||||
ret = lsetxattrat (chdir_fd, file_name, 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, file_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -562,38 +618,42 @@ xattrs__fd_set (struct tar_stat_info const *st,
|
||||
/* 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;
|
||||
@@ -612,11 +672,10 @@ xattrs_selinux_set (struct tar_stat_info const *st,
|
||||
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, file_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -624,23 +683,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)
|
||||
@@ -650,7 +706,7 @@ xattrs_kw_included (const char *kw, bool archiving)
|
||||
}
|
||||
|
||||
static bool
|
||||
xattrs_kw_excluded (const char *kw, bool archiving)
|
||||
xattrs_kw_excluded (const char *kw)
|
||||
{
|
||||
return xattrs_setup.excl.size ?
|
||||
xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
|
||||
@@ -662,31 +718,29 @@ xattrs_kw_excluded (const char *kw, bool archiving)
|
||||
static bool
|
||||
xattrs_masked_out (const char *kw, bool archiving)
|
||||
{
|
||||
return xattrs_kw_included (kw, archiving) ?
|
||||
xattrs_kw_excluded (kw, archiving) : true;
|
||||
return xattrs_kw_included (kw, archiving) ? xattrs_kw_excluded (kw) : true;
|
||||
}
|
||||
|
||||
void
|
||||
xattrs_xattrs_set (struct tar_stat_info const *st,
|
||||
char const *file_name, char typeflag, int later_run)
|
||||
char const *file_name, char typeflag, 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 scan = 0;
|
||||
|
||||
if (!st->xattr_map_size)
|
||||
if (!st->xattr_map.xm_size)
|
||||
return;
|
||||
|
||||
for (; scan < st->xattr_map_size; ++scan)
|
||||
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
|
||||
{
|
||||
char *keyword = st->xattr_map[scan].xkey;
|
||||
keyword += strlen ("SCHILY.xattr.");
|
||||
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
|
||||
|
||||
/* TODO: this 'later_run' workaround is temporary solution -> once
|
||||
capabilities should become fully supported by it's API and there
|
||||
@@ -695,16 +749,16 @@ 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"))
|
||||
&& later_run == (strcmp (keyword, "security.capability") != 0))
|
||||
continue;
|
||||
|
||||
if (xattrs_masked_out (keyword, false /* extracting */ ))
|
||||
/* we don't want to restore this keyword */
|
||||
continue;
|
||||
|
||||
xattrs__fd_set (st, file_name, typeflag, keyword,
|
||||
st->xattr_map[scan].xval_ptr,
|
||||
st->xattr_map[scan].xval_len);
|
||||
xattrs__fd_set (file_name, typeflag, keyword,
|
||||
st->xattr_map.xm_map[i].xval_ptr,
|
||||
st->xattr_map.xm_map[i].xval_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -713,25 +767,23 @@ 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_size)
|
||||
for (i = 0; i < st->xattr_map_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[i].xkey + strlen ("SCHILY.xattr.");
|
||||
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
|
||||
if (!xattrs_masked_out (keyword, false /* like extracting */ ))
|
||||
{
|
||||
*output = '*';
|
||||
@@ -768,16 +820,14 @@ xattrs_print (struct tar_stat_info const *st)
|
||||
}
|
||||
|
||||
/* xattrs */
|
||||
if (xattrs_option > 0 && st->xattr_map_size)
|
||||
if (xattrs_option && st->xattr_map.xm_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < st->xattr_map_size; ++i)
|
||||
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
|
||||
{
|
||||
char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
|
||||
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[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-2019 Free Software Foundation, Inc.
|
||||
Copyright (C) 2006-2025 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 fd, 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);
|
||||
|
||||
606
src/xheader.c
606
src/xheader.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# Makefile for GNU tar regression tests.
|
||||
|
||||
# Copyright 1996-2019 Free Software Foundation, Inc.
|
||||
# Copyright 1996-2025 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,20 +71,24 @@ 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\
|
||||
delete04.at\
|
||||
delete05.at\
|
||||
delete06.at\
|
||||
difflink.at\
|
||||
dirrem01.at\
|
||||
dirrem02.at\
|
||||
exclude.at\
|
||||
exclude01.at\
|
||||
exclude02.at\
|
||||
@@ -99,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\
|
||||
@@ -120,13 +131,20 @@ TESTSUITE_AT = \
|
||||
extrac19.at\
|
||||
extrac20.at\
|
||||
extrac21.at\
|
||||
extrac22.at\
|
||||
extrac23.at\
|
||||
extrac24.at\
|
||||
extrac25.at\
|
||||
extrac26.at\
|
||||
extrac27.at\
|
||||
extrac28.at\
|
||||
extrac29.at\
|
||||
extrac30.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\
|
||||
@@ -138,8 +156,8 @@ TESTSUITE_AT = \
|
||||
incr09.at\
|
||||
incr10.at\
|
||||
incr11.at\
|
||||
incremental.at\
|
||||
indexfile.at\
|
||||
ignfail.at\
|
||||
label01.at\
|
||||
label02.at\
|
||||
label03.at\
|
||||
@@ -183,21 +201,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\
|
||||
recurse.at\
|
||||
remfiles01.at\
|
||||
remfiles02.at\
|
||||
remfiles03.at\
|
||||
@@ -220,12 +233,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\
|
||||
@@ -241,6 +263,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\
|
||||
@@ -248,21 +277,12 @@ TESTSUITE_AT = \
|
||||
update01.at\
|
||||
update02.at\
|
||||
update03.at\
|
||||
volsize.at\
|
||||
volume.at\
|
||||
update04.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\
|
||||
@@ -270,12 +290,12 @@ TESTSUITE_AT = \
|
||||
xattr05.at\
|
||||
xattr06.at\
|
||||
xattr07.at\
|
||||
acls01.at\
|
||||
acls02.at\
|
||||
acls03.at\
|
||||
selnx01.at\
|
||||
selacl01.at\
|
||||
capabs_raw01.at
|
||||
xattr08.at\
|
||||
xform-h.at\
|
||||
xform01.at\
|
||||
xform02.at\
|
||||
xform03.at\
|
||||
xform04.at
|
||||
|
||||
distclean-local:
|
||||
-rm -rf download
|
||||
@@ -283,7 +303,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 $@
|
||||
|
||||
@@ -323,4 +343,8 @@ AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/lib\
|
||||
-DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
LDADD = ../gnu/libgnu.a $(LIBINTL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
|
||||
LDADD = ../gnu/libgnu.a\
|
||||
$(LIB_ACL) $(QCOPY_ACL_LIB) $(CLOCK_TIME_LIB) $(EUIDACCESS_LIBGEN)\
|
||||
$(GETRANDOM_LIB) $(HARD_LOCALE_LIB) $(FILE_HAS_ACL_LIB) $(MBRTOWC_LIB)\
|
||||
$(LIB_SELINUX) $(SETLOCALE_NULL_LIB) \
|
||||
$(LIBINTL) $(LIBICONV)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Process this file with autom4te to create testsuite. -*- Autotest -*-
|
||||
#
|
||||
# Test suite for GNU tar.
|
||||
# Copyright 2013-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2014-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2014-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2006-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2015-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2015-2025 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-2019 Free Software Foundation, Inc.
|
||||
# Copyright 2013-2025 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
Reference in New Issue
Block a user