Compare commits

...

298 Commits

Author SHA1 Message Date
Paul Eggert
8fca6143ea maint: pacify -Wzero-as-null-pointer-constant
Recent Gnulib enables this warning, and it did find a bug
in GNU Tar, so pacify GCC everywhere else by preferring
NULL to 0 for pointers.
2026-01-23 17:38:38 -08:00
Paul Eggert
29bb75ed65 Fix EOF return from wordsplit_finish
* lib/wordsplit.c (wordsplit_finish):
Fix typo caught by -Wzero-as-null-pointer-constant
2026-01-23 17:38:38 -08:00
Paul Eggert
bd20771003 Sync bootstrap from gnulib 2026-01-23 17:38:38 -08:00
Paul Eggert
f1efd80ebe build: update gnulib and paxutils submodules to latest
* src/extract.c: Include issymlinkat.h, not issymlink.h.
2026-01-23 17:38:38 -08:00
Paul Eggert
d55c5fd2c5 Update copyright years
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
$HOME/src/gnu/gnulib/build-aux/update-copyright \
  $(git ls-files | sed -e '/^gnulib$/d
			   /^paxutils$/d
			   /^COPYING$/d
			   /\/fdl.texi$/d')
sed -i '2000,${
    /^Copyright @copyright/d
    s/^[0-9]*--\(2026 Free Software Foundation, Inc.\)/Copyright (C) \1/
  }' doc/tar.texi
2026-01-23 17:38:38 -08:00
Paul Eggert
505cf47a0a Fix commit typo when bringing back placeholders
Problem reported by Pavel Raiskup in:
https://lists.gnu.org/r/bug-tar/2025-11/msg00028.html
* src/extract.c (contains_dot_dot): Bring back this function here,
from its former location in src/names.c.  Make it static since
it is used only in this compilation unit.
2025-11-27 11:21:49 -08:00
Paul Eggert
f83a120c58 Bring back placeholders
They can still be useful if -h is used.  See Pavel Cahyna in:
https://lists.gnu.org/r/bug-tar/2025-11/msg00026.html
while we’re at it bring them back if -P is used,
as they can still be useful there too.
* src/extract.c (HAVE_BIRTHTIME, BIRTHTIME_EQ):
Bring back these macros.
(struct delayed_link, struct string_list):
Bring back these structs.
(delayed_link_table, delayed_link_head, delayed_link_tail):
Bring back these static vars.
(dl_hash, dl_compare, find_direct_ancestor)
(find_delayed_link_source, create_placeholder_file)
(apply_delayed_link, apply_delayed_links):
Bring back these static functions.
(mark_metadata_set): Rename from mark_after_links.  All uses changed.
(extract_link, extract_symlink):
Create placeholders as before, except only if -P or -h are used.
(extract_finish): Deal with delayed links, as before.
2025-11-26 20:49:38 -08:00
Paul Eggert
2bbc58bf0b Support gnulib-style timestamps in checkpoint logs
* gnulib.modules: Add nstrftime-limited, time_rz.  Sort.
* src/checkpoint.c: Include <strftime.h>.
(format_checkpoint_string): Use nstrftime instead of strftime.
Also fix an obscure bug on platforms that lack tm_gmtoff+tm_zone by
calling tzalloc on those platforms; if it fails, fall back on gmtime.
Also, use fwrite instead of fprintf, since we typically know the
length already and this gives us a more-accurate byte count
in case there are partial writes.
2025-11-23 09:51:31 -08:00
Paul Eggert
85d99f18af build: update gnulib submodule to latest 2025-11-23 09:51:31 -08:00
Paul Eggert
db65c2dd68 Port to C23 qualifier-generic fns like strchr
* src/checkpoint.c (getarg):
* src/tar.c (expand_pax_option):
Const-qualify results of strchr etc. if args are const-qualified.
2025-11-23 09:51:31 -08:00
Paul Eggert
28556dddae build: update gnulib submodule to latest 2025-11-17 16:40:18 -08:00
Paul Eggert
cdc541ad52 Prefer countof to sizeof / sizeof
C2y plans to introduce a new countof operator that will be
convenient for GNU tar, so start using it now via Gnulib.
* gnulib.modules: Add stdcountof-h.
* lib/wordsplit.c, src/buffer.c, src/suffix.c, src/tar.c:
Include stdcountof.h, and prefer countof (X) to sizeof X / sizeof *X.
2025-11-15 15:50:05 -08:00
Paul Eggert
2e243986c7 Port new extraction test to FreeBSD 15
* tests/extrac31.at (extracting untrusted incremental):
Port to FreeBSD 15 wording.
2025-11-15 15:10:48 -08:00
Paul Eggert
db9ca8d754 Port to compilers where COMMON_INLINE is static
Problem found with clang 15.0 on CheriBSD.
* src/names.c (namelist_match, register_match):
Now plain static, not static COMMON_INLINE, since the later
could mean the declaration is ‘static static’ which is not allowed.
2025-11-15 15:10:48 -08:00
Paul Eggert
b53b39209e Pacify clang 14 -Wbitwise-conditional-parentheses
* src/common.h (add_printf):
* src/sparse.c (decode_num):
Parenthesize to pacify Apple clang version 14.0.0 (clang-1400.0.29.202).
2025-11-15 15:10:48 -08:00
Paul Eggert
8d8e441bb5 Skip sparse test on non-sparse file systems
* tests/sparse05.at (listing sparse files bigger than 2^33 B):
If there were problems generating BIGFILE remove it,
as it has likely exhausted the file system.
Problem found on Darwin 21.6 APFS.
2025-11-15 15:10:48 -08:00
Paul Eggert
453d903de9 Adjust to Gnulib strftime changes for macOS
Stop using the fprintftime module, as as with recent Gnulib changes
it breaks the build on macOS, and fixing this would drag in threading
libraries and macOS-specific libraries that are overkill for tar.
Instead, just use strftime; that’s good enough here and arguably
better in case someone attacks tar with a huge time format string.
* gnulib.modules: Remove fprintftime.
* src/checkpoint.c: Do not include fprintftime.
(format_checkpoint_string): Always output some useful info (a decimal
seconds count), even if localtime fails. Do not output more than
256 bytes of time info, as that’s likely a DoS attack.  Stick with
plain strftime, as fprintftime’s extra features are overkill here.
2025-11-15 15:10:48 -08:00
Paul Eggert
0521528cdf Port intmax macro to strict C11
Needed for Apple clang version 14.0.0 (clang-1400.0.29.202).
* src/common.h (intmax): Define macro only if _Generic works, and
use _Generic in it rather that using EXPR_SIGNED.  This is needed
to make the first argument of verify_expr an integer constant
expression, which is required for strict C11.  Although GCC is
smart enough to treat (1 ? 0 : V) as an integer constant
expression even if V is an integer variable, C11 does not require
support for this sort of thing.
2025-11-15 15:10:48 -08:00
Paul Eggert
4e548d150c Port new tests to Solaris 10 strerror
* tests/extrac31.at: Adjust to Solaris 10 diagnostics.
2025-11-15 15:10:48 -08:00
Paul Eggert
50b559c3d7 Do not create empty placeholder files
* src/extract.c (HAVE_BIRTHTIME, BIRTHTIME_EQ, struct delayed_link)
(delayed_link_table, delayed_link_head delayed_link_tail)
(struct string_list, dl_hash, dl_compare, find_direct_ancestor)
(find_delayed_link_source, create_placeholder_file, apply_delayed_link)
(apply_delayed_links): Remove.  All uses removed.
(struct delayed_set_stat): New member metadata_set,
replacing after_links.  All uses changed.
(apply_nonancestor_delayed_set_stat): Arg METADATA_SET replaces
the old AFTER_LINKS.  All callers changed.
(extract_archive): Do not worry about "..", since openat2
now does that for us.
* src/names.c (first_dot_dot): Remove.  All uses removed.
2025-11-15 15:10:48 -08:00
Paul Eggert
75b03fdff4 Use openat2 to jailify the extraction directory
This addresses CVE-2025-45582.
* gnulib.modules: Add openat2.
* src/misc.c (open_subdir): New static function.
(fdbase_opendir): Use it.
* src/tar.c (open_searchdir_how): New var, replacing and
augmenting open_searchdir_flags.  All uses changed.
* tests/extrac31.at: New file.
* tests/Makefile (TESTSUITE_AT), tests/testuite.at: Add it.
2025-11-15 15:10:48 -08:00
Paul Eggert
aec5d77437 Omit trailing white space and empty lines 2025-11-15 15:10:48 -08:00
Paul Eggert
8ba9e244e8 Standardize on “working directory” 2025-11-15 15:10:48 -08:00
Paul Eggert
e1445cfdf0 Use fewer flags when opening directories
This doesn’t change behavior; it is a refactoring for
compatibility with a future patch that will use Linux’s
openat2 syscall, which is pickier about flags.
* src/tar.c (decode_options): When searching directories,
do not use O_NOCTTY, O_NONBLOCK, and O_NOATIME.
openat2 rejects all three flags if O_PATH is used.
The first two flags are definitely irrelevant for directories,
and O_NOATIME probably doesn’t matter either.
2025-11-15 15:10:48 -08:00
Paul Eggert
915a8077af Improve performance of relative opendir
* src/misc.c (fdbase_opendir): When the new directory is a
subdirectory of the old one, open relative to the old one rather
than relative all the way back to chdir_fd, and if that open fails
for a non-EMFILE reason, keep rather than discard the old directory.
2025-11-15 15:10:48 -08:00
Paul Eggert
cdb586803b Work around Oracle Developer Studio compiler bug
* src/create.c (dump_file0): Reword.
2025-11-15 15:10:48 -08:00
Paul Eggert
bdd773d028 Cache parent directories
Although this might help (or hurt) performance, the main
motivation is to make it easier in future commits
to prevent tarballs from escaping the extraction directory.
* src/common.h: (BADFD): New constant.
(struct fdbase): New type.
* src/create.c (dump_file0): Use parent->fd instead of caching
it into a local, as the latter approach is now awkward.
* src/extract.c (extract_link): Don’t save errno unless needed.
* src/misc.c (safer_rmdir): New arg F.  All callers changed.
(maybe_backup_file): Construct full after_backup_name, now
that find_backup_file_name no longer does that for us.
(chdir_fd): Now static not extern, as other modules now use fdbase.
(fdbase_cache): New static var.
(fdbase_clear): New function.  Call it whenever removing
or renaming directories or symlinks to directories.
(fdbase_opendir): New static function.
(fdbase, fdbase1): New functions.  Call them whenever the
code formerly passed chdir_fd to a syscall.
2025-11-15 15:10:48 -08:00
Paul Eggert
382a47f2fd Prefer issymlinkat
* gnulib.modules: Add issymlinkat, already an indirect dependency.
* src/extract.c: Include issymlink.h.
(is_directory_link, open_output_file):
Prefer issymlinkat to doing it by hand.
2025-11-15 15:10:48 -08:00
Paul Eggert
a109947a78 Make xclose static
* src/buffer.c (xclose): Move from here ...
* src/system.c: ... to here, and make it static.
2025-11-15 15:10:48 -08:00
Paul Eggert
8fca2d35e8 Prefer O_PATH to O_SEARCH on Linux kernels
* src/tar.c (decode_options): Prefer O_PATH to an O_SEARCH that is
actually O_RDONLY.
2025-11-15 15:10:48 -08:00
Paul Eggert
238250f19e Prefer streq/memeq when they will do
Gnulib’s new streq and memeq functions make code a bit more
readable and, we hope, a bit more reliable and easy to maintain.
* gnulib.modules: Add stringeq.
* lib/wordsplit.c (wordsplit_find_env):
* src/buffer.c (check_compressed_archive, check_tty)
(_open_archive, new_volume, try_new_volume)
(drop_volume_label_suffix):
* src/checkpoint.c (checkpoint_compile_action):
* src/compare.c (process_rawdata, diff_symlink):
* src/create.c (cachedir_file_p):
* src/delete.c (delete_archive_members):
* src/exclist.c (hg_addfn, get_vcs_ignore_file):
* src/extract.c (ds_compare, remove_delayed_set_stat)
(fixup_delayed_set_stat, apply_nonancestor_delayed_set_stat)
(extract_link):
* src/incremen.c (nfs_file_stat, compare_directory_canonical_names)
(procdir):
* src/list.c (read_header, decode_header):
* src/misc.c (replace_prefix):
* src/names.c (uname_to_uid, gname_to_gid, read_next_name)
(name_compare):
* src/sparse.c (check_data_region):
* src/suffix.c (find_compression_suffix):
* src/system.c (sys_detect_dev_null_output)
(sys_child_open_for_compress, sys_child_open_for_uncompress):
* src/tar.c (set_archive_format, tar_set_quoting_style)
(optloc_eq, set_use_compress_program_option, decode_signal)
(report_textual_dates, decode_options):
* src/update.c (update_archive):
* src/warning.c (set_warning_option):
* src/xattrs.c (xattrs_xattrs_set):
* src/xheader.c (xheader_keyword_override_p)
(xheader_set_keyword_equal, locate_handler)
(xheader_protected_keyword_p):
Prefer memeq/streq to memcmp/strcmp when either will do.
2025-11-15 15:10:48 -08:00
Paul Eggert
7c241126f1 Refactor to avoid duplication in "./" scanning
* src/exclist.c (excluded_name):
* src/misc.c (normalize_filename_x, must_be_dot_or_slash)
(chdir_arg):
Use dotslash or dotslashlen instead of doing things by hand.
* src/misc.c (slashlen, dotslashlen): New functions.
(safer_rmdir): Do not worry about unlinkat with AT_REMOVEDIR
succeeding on ".", as POSIX prohibits it, and it does not succeed
on any known platform. This simplifies the file name test.
Continue to worry about "/" though, as POSIX does allow
it to be removed.
2025-11-15 15:10:48 -08:00
Paul Eggert
56fb4a96ca chdir_id refactoring
This prepares for future changes that need directory IDs.
* src/common.h (struct chdir_id): New struct.
* src/extract.c (extract_dir): Use chdir_id to avoid duplicate stats.
* src/misc.c (struct wd): New member ID.
(grow_wd): New function, extracted from chdir_arg and that
also initializes id.err.
(chdir_arg): Use it.  Initialize id.err.
(chdir_id): New function.
2025-11-15 15:10:48 -08:00
Paul Eggert
83cad5835f Remove unreachable assignment to mtime.tv_nsec
Caught by Oracle Developer Studio 12.6
* src/incremen.c (read_incr_db_01): Remove unreachable code.
2025-11-15 15:10:48 -08:00
Paul Eggert
58b471f14a Omit duplicate declaration of ‘usage’ 2025-11-15 15:10:48 -08:00
Paul Eggert
f8b087a9f8 * README-hacking: Add testing note. 2025-11-15 15:10:48 -08:00
Paul Eggert
109edc9edb Fix test to match paxutils quoting change
* tests/incr08.at (filename normalization):
Adjust test to match quoting change in paxutils.
2025-11-15 15:10:48 -08:00
Paul Eggert
08e42808b6 Use Gnulib gendocs instead of our own
I ported our fix into Gnulib so there’s no longer a need
for a separate copy.
* doc/Makefile.am (GENDOCS): Now in ../build-aux, not here.
* doc/gendocs.sh, doc/gendocs_template: Remove.
* gnulib.modules: Add gendocs.
2025-11-15 15:10:48 -08:00
Paul Eggert
0b7d124e36 maint: sync bootstrap, fdl.texi from Gnulib 2025-11-15 15:10:48 -08:00
Paul Eggert
ca6af3a7f5 build: update gnulib and paxutils submodules to latest 2025-11-15 15:10:48 -08:00
Matteo Croce
92f040151c fix build error when compiling with --without-xattrs
* src/extract.c (set_xattr):
* src/xattrs.c (xattrs_xattrs_add, xattrs_xattrs_get, xattrs_xattrs_set):
Add MAYBE_UNUSED.
Copyright-paperwork-exempt: yes
2025-11-12 17:21:42 -08:00
David Leadbeater
5def5cb369 Quote arguments in diagnostic messages.
Copyright-paperwork-exempt: yes
2025-11-12 13:11:00 +02:00
Sergey Poznyakoff
f501cf8c9a Version 1.35.90 2025-10-19 09:39:14 +03:00
Collin Funk
2737e1aec0 maint: Update library names used by Gnulib.
* src/Makefile.am (tar_LDADD):
* tests/Makefile.am (LDADD): Update library names according to Gnulib.

Copyright-paperwork-exempt: yes
2025-08-19 21:12:59 -07:00
Paul Eggert
ca02de4050 Avoid overrun when converting ns-resolution timestamps to text
Caught by gcc -fsanitize=address.
Inspired by Matthias Andree’s bug report in:
https://lists.gnu.org/r/bug-tar/2025-08/msg00019.html
though I found this bug via a simple "make check"
with sanitization enabled.
* src/common.h (TIMESPEC_STRSIZE_BOUND):
Make room for leading '-', needed in addition to the '-' room
supplied by SYSINT_BUFSIZE due to the way code_timespec works.
2025-08-18 17:14:49 -07:00
Paul Eggert
ea7cfcba77 Avoid hash_meta_directory int overflow
* src/incremen.c (hash_directory_meta):
Avoid possibility of signed integer overflow.
2025-08-14 10:27:28 -07:00
Paul Eggert
bdc442bd5c Use Gnulib’s same-inode module
This is more portable to non-POSIX systems.
However, don’t bother trying to port to systems
where st_ino is not a scalar of type dev_t,
as these systems no longer seem to be active targets
and it’s not worth the maintenance hassle.
* gnulib.modules: Add same-inode, now that we use it
explicitly rather than indirectly.
* src/compare.c (diff_link):
* src/create.c (compare_links, restore_parent_fd):
* src/incremen.c (compare_directory_meta, procdir):
* src/extract.c (dl_compare, repair_delayed_set_stat)
(apply_nonancestor_delayed_set_stat, extract_link)
(apply_delayed_link):
* src/names.c (add_file_id):
* src/system.c (sys_file_is_archive, sys_detect_dev_null_output):
Include same-inode.h, and prefer its macros and functions
to doing things by hand.
* src/create.c (struct link):
* src/extract.c (struct delayed_set_stat, struct delayed_link):
* src/incremen.c (struct directory):
* src/names.c (struct file_id_list):
Rename members to st_dev and st_ino so that SAME_INODE and
PSAME_INODE can be used on the type.  All uses changed.
* src/system.c (sys_compare_links): Remove.
All uses replaced by psame_inode.
2025-08-14 10:27:28 -07:00
Collin Funk
5402831d62 manual: remove '.info' suffix in manual names passed to @xref
Texinfo 7.2 began warning about the '.info' suffix in the manual names
passed to @xref and similar commands.  They eventually plan to stop
stripping the '.info' suffix internally which will lead to broken links
in the manuals without this change.

* doc/tar.texi (files): Remove '.info' suffix from manual name.
2025-08-07 23:56:50 -07:00
Paul Eggert
4e742fc867 --no-overwrite-dir no overwrite even temporarily
Problem and fix reported by Pavel Cahyna in
https://lists.gnu.org/r/bug-tar/2025-01/msg00000.html
* src/extract.c (extract_dir): With --no-overwrite-dir,
skip the chmod if the directory already exists.
* tests/extrac23.at (--no-overwrite-dir on empty directory):
Move the part of the test that looks at a nonempty directory ...
* tests/extrac30.at: ... to this new file, because the test now
must be run as non-root.  Adjust the test to match the new behavior.
* tests/Makefile.am (TESTSUITE_AT), tests/testsuite.at: Add it.
2025-07-26 21:49:20 -07:00
Paul Eggert
076818f8d9 Use flexible array member in struct dumpdir
* src/incremen.c: Include flexmember.h.
(struct dumpdir): contents is now a flexible member, not a pointer.
This is more idiomatic and slightly more efficient.
(dumpdir_create0): Adjust to the new struct dumpdir layout.
2025-07-26 02:20:53 -07:00
Paul Eggert
c11084bcc2 Avoid undefined behavior in magic checking
* src/buffer.c (check_compressed_archive):
* src/list.c (read_header, decode_header):
Use memcmp, not strcmp, when looking for magic strings in
headers, since input headers are not guaranteed to be
strings and strcmp has undefined behavior otherwise.
2025-07-26 02:20:53 -07:00
Paul Eggert
75735940f1 Port more code to UBSan, and fix alignment bug
Problem with extract_file reported by Kirill Furman in:
https://lists.gnu.org/r/bug-tar/2025-07/msg00003.html
Since the UBSan thing seems to be a recurring issue,
I fixed other instances of the problem that I found.
Also, I noticed that the same line of code had another failure to
conform to C23’s rules for pointers (an alignment issue not caught
by UBSan), so I fixed that too.  None of these issues matter on
practical production hosts.
* src/common.h (charptr): New function.
* src/buffer.c (available_space_after, short_read, flush_archive)
(backspace_output, try_new_volume, simple_flush_read)
(_gnu_flush_read, _gnu_flush_write):
* src/compare.c (read_and_process):
* src/create.c (write_eot, write_gnu_long_link)
(dump_regular_file, dump_dir0):
* src/extract.c (extract_file):
* src/incremen.c (get_gnu_dumpdir):
* src/list.c (read_header):
* src/sparse.c (sparse_dump_region, sparse_extract_region):
* src/system.c (sys_write_archive_buffer)
(sys_child_open_for_compress, sys_child_open_for_uncompress):
* src/update.c (append_file, update_archive):
Use it.
* src/buffer.c (set_next_block_after): Arg is now void *,
not union block *, since it need not be a valid union block * pointer
and this can matter on unusual or debugging implementations.
Turn a loop into an if so that the code is O(1) not O(N).
2025-07-26 02:20:53 -07:00
Paul Eggert
8921131877 Pacify gcc -Wunterminated-string-initialization
This diagnostic is new to GCC 15.
* src/create.c (cachedir_file_p): Mark local as nonstring.
2025-07-14 08:17:12 -07:00
Paul Eggert
aecf7146d3 Sync bootstrap from Gnulib 2025-06-20 14:17:04 -07:00
Paul Eggert
1ad538b359 build: update gnulib and paxutils submodules to latest 2025-06-20 14:17:04 -07:00
Paul Eggert
7d96e820a5 Port short_read to UBSan
Problem reported by Kirill Furman in:
https://lists.gnu.org/r/bug-tar/2025-06/msg00002.html
* src/buffer.c (short_read): Use (char *) record_start,
instead of record_start->buffer, to avoid undefined behavior
accessing past end of buffer.  In practice the undefined
behavior is harmless unless running with -fsanitize=undefined
or a similarly-picky implementation.
2025-06-12 00:21:48 -07:00
Sergey Poznyakoff
7c4f8fb579 Bugfix
* src/names.c (all_names_found): Exempt wildcard entries from hierarchy
checking.
2025-06-02 07:35:46 +03:00
Sergey Poznyakoff
4303066730 Fix spurious "Not found in archive" errors.
* src/names.c (namelist_match_from): New function.
(namelist_match): Rewrite as a wrapper over it.
(register_match): New function.
(name_match)" Update all possible matches in the name list.

* tests/extrac29.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Likewise.
2025-05-14 15:28:55 +03:00
Sergey Poznyakoff
9324b472b0 Minor changes 2025-05-13 17:59:15 +03:00
Sergey Poznyakoff
b009124ffd Handle directory members consistently when listing and when extracting.
* src/list.c (skim_member): Recognize directory members using
the same rules as during extraction.
* tests/skipdir.at: New testcase.
* tests/testsuite.at: Add new test.
* tests/Makefile.am: Likewise.
2025-05-12 17:20:27 +03:00
Anssi Hannula
827dde1605 Fix missing type in mknodat() mode argument
Per POSIX, the type of the file to be created should be OR'ed to the
`mode` argument of mknodat().

However, set_xattr() creates an empty file using mknodat() and does not
do that.

E.g. Linux kernel considers zero type as S_IFREG, so the code works on
most systems.

However, e.g. fakeroot, at least up to the current v1.36, does not
consider 0 as S_IFREG, instead creating an invalid file, causing tar to
enter an infinite retry loop when trying to create a file with xattrs
under fakeroot.

Since set_xattr is used only when extracting regular files, fix that
by ORing its mode argument with S_IFREG.

Copyright-paperwork-exempt: Yes
2025-05-12 13:18:21 +03:00
Tobias Stoeckmann
65228e9ba9 Fix typos
Copyright-paperwork-exempt: Yes
2025-05-12 11:23:21 +03:00
xiangjingsi
e36d3354c7 Fix restoring extended attributes from global PAX headers
Copyright-paperwork-exempt: Yes
2025-05-08 22:58:15 +03:00
Sergey Poznyakoff
d175e21b7f Upgrade paxutils. 2025-05-07 08:33:23 +03:00
Sergey Poznyakoff
c0fce47363 Fix typo 2025-05-06 22:40:02 +03:00
Sergey Poznyakoff
807e340ab2 Minor fix
* src/extract.c (set_mode): Re-stat the file if current_mode_mask
bits tell so.
2025-05-06 22:29:29 +03:00
Sergey Poznyakoff
6131dd2805 Skip file or archive member if its transformed name is empty.
* NEWS: Document changes.
* doc/tar.texi: Document changes.
* src/common.h (transform_stat_info): Change return value.
(transform_name_fp): Change signature.
(WARN_EMPTY_TRANSFORM): New constant.
* src/create.c: Check return from transform_name.  Skip file, if it
is false.
* src/list.c (transform_stat_info): Return bool.
(read_and): Skip member if transform_stat_info returns false.
* src/transform.c (_transform_name_to_obstack): Change return type.
Always allocate result in obstack.
(transform_name_fp): Change arguments.  Return true on
success (transformed string not empty).  Otherwise return false and
don't change the source string.
* src/warning.c: New warning class: empty-transform.
* tests/extrac17.at: Use --warning=empty-transform.
2025-05-06 15:32:17 +03:00
Sergey Poznyakoff
bfc3346394 Minor fix
* src/list.c (transform_stat_info): Modify argument pointer,
not the global variable.
2025-05-06 09:39:38 +03:00
Paul Eggert
b5f4948ce4 Port to recent Gnulib hash_remove
Problem reported by Bruno Haible in:
https://lists.gnu.org/r/bug-tar/2025-04/msg00003.html
* src/incremen.c (remove_directory): hash_delete → hash_remove.
2025-04-29 14:05:59 -07:00
Sergey Poznyakoff
cd1f6624f7 Fix restoring permissions of intermediate directories with --skip-old-files
Detailed bug report: https://savannah.gnu.org/bugs/index.php?66774

* src/extract.c (update_interdir_set_stat): New function.
(extract_dir): If the directory already exists, check if it
has been created as intermediate directory earlier.  If so,
update its delayed_set_stat data from archive.

* tests/Makefile.am: Add new testcase.
* tests/testsuite.at: Add new testcase.
* tests/extrac28.at: New file.
2025-03-14 15:07:27 +02:00
Sergey Poznyakoff
55ecb28315 documentation: remove incorrect statement
Reported in https://savannah.gnu.org/bugs/index.php?66704
2025-03-14 09:10:01 +02:00
Paul Eggert
31d84e2f67 doc: mention timestamp limits 2025-01-29 11:56:59 -08:00
Paul Eggert
2e41cdce6d Adjust to recent Gnulib module renaming
* gnulib.modules: stdbool was renamed to bool, etc.
2025-01-01 18:33:10 -08:00
Paul Eggert
ff9d7ec77b build: update gnulib and paxutils submodules to latest 2025-01-01 18:33:10 -08:00
Paul Eggert
4a9a4c16e1 doc: fix man page copyright notice
* doc/tar.1: Put copyright notice where the update procedure
will update it properly, and fix it.
2025-01-01 18:33:10 -08:00
Paul Eggert
0aa991f386 Update copyright years
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
$HOME/src/gnu/gnulib/build-aux/update-copyright \
  $(git ls-files | sed -e '/^gnulib$/d
			   /^paxutils$/d
			   /^COPYING$/d
			   /\/fdl.texi$/d')
sed -i '2000,${
    /^Copyright @copyright/d
    s/^[0-9]*--\(2025 Free Software Foundation, Inc.\)/Copyright (C) \1/
  }' doc/tar.texi
2025-01-01 18:33:10 -08:00
Paul Eggert
53f7e6aa62 tests: port to test dirs where pwd != pwd -P
Problem reported by Bruno Haible in:
https://lists.gnu.org/r/bug-tar/2024-12/msg00003.html
* tests/incr08.at: Use pwd -P, not plain pwd.
2024-12-27 14:06:14 -08:00
Paul Eggert
c3f93039ca tests: port to testing in Linux /tmp
* tests/selnx01.at: Discard restorecon output,
as it contains "Warning no default label for ..."
when run in Linux tmpfs on Fedora 40.
2024-12-27 14:06:14 -08:00
Paul Eggert
d2b6b7b0a7 Fix bad pointer usage in xsparse.c
* scripts/xsparse.c (read_xheader): Avoid undefined behavior by
accessing via null pointer sparse_map or out of its bounds when
the input is invalid.  This means we no longer need the ‘expect’
local, so omit it for simplicity.
2024-11-06 10:18:55 -08:00
Paul Eggert
9bbcac1cf7 Port xsparse.c to AIX
* scripts/xsparse.c (emalloc): Do not report failure when malloc
(0) returns NULL, as it does on AIX.  Simply return a null
pointer; that’s good enough for xsparse.c.
2024-11-06 10:18:55 -08:00
Paul Eggert
ac06d4d104 Fix xsparse.c big heap allocation bugs
* scripts/xsparse.c (expand_sparse): Read into auto buffer, not heap.
The heap code was wrong for two reasons: it called malloc just once
in the try-again loop, and even when it succeeded it could have
left so few bytes available in the heap that later stdio calls
could fail.  Reading into the auto buffer might be a bit slower
but speed is not an issue here and it’s better to be simple.
2024-11-06 10:18:55 -08:00
Sergey Poznyakoff
a855a80d06 Remove non-ASCII comment text 2024-11-04 08:34:29 +02:00
Sergey Poznyakoff
b5bf1ccd18 Update paxutils 2024-11-04 08:31:28 +02:00
Paul Eggert
a6cf78b0fa Add LG_BLOCKSIZE to omit some *, % ops
* src/buffer.c (_flush_write, short_read, seek_archive)
(_gnu_flush_write):
* src/create.c (write_gnu_long_link, dump_regular_file)
(dump_dir0):
* src/delete.c (write_recent_bytes, flush_file)
(delete_archive_members):
* src/list.c (read_header):
* src/sparse.c (sparse_dump_region, sparse_extract_region)
(pax_dump_header_1):
* src/tar.c (parse_opt):
* src/update.c (append_file):
Prefer shifting and masking to dividing and remaindering by
BLOCKSIZE.  This reclaims some compiler optimizations lost
by our recent preference for signed integers.
* src/tar.h (LG_BLOCKSIZE): New constant, for shifting.
2024-11-02 13:43:05 -07:00
Paul Eggert
568919d77b Improve sparse I/O performance
* src/sparse.c (sparse_dump_region, sparse_extract_region):
Don’t insist on reading and writing sparse files 512
bytes at a time.  This resulted in a 4× to 6× performance
improvement on my platform.
2024-11-02 13:43:05 -07:00
Paul Eggert
c500103600 Simplify read_incr_db_01 malloc
* src/incremen.c (read_incr_db_01): Replace arg initbuf with two
args pbuf and pbufsize so that we can simplify memory allocation.
Caller changed.  Omit now-unnecessary free, xstrdup, strlen.
2024-11-02 13:43:05 -07:00
Paul Eggert
5c47fcf187 Avoid malloc in change_tape_menu
* src/buffer.c (change_tape_menu): Avoid unnecessary xstrdup.
2024-11-02 13:43:05 -07:00
Paul Eggert
005f2916b6 Improve common.h comment 2024-11-02 13:43:05 -07:00
Paul Eggert
15d35a0f61 Count short read slop when seeking
* src/buffer.c (short_read_slop): New static var.
(get_archive_status): Treat anything other than fifos and sockets
as potentially seekable; they’ll tell us if they aren’t, whereas
fifos and sockets cannot be seekable.  Check named files for
initial offset too, to deal with names like /dev/stdin.
Do not worry about start_offset’s value if !seekable_archive,
as it won’t be used.  Use short_read_slop.
(short_read, try_new_volume, simple_flush_read, _gnu_flush_read):
Set short_read_slop.
2024-11-02 13:43:05 -07:00
Paul Eggert
04b4f491a8 Prefer other types to int in xattrs.c
* src/xattrs.c (xattrs__acls_set) [HAVE_POSIX_ACLS]:
Prefer acl_type_t to int for ACL types.
(acls_get_text, xattrs_acls_get, xattrs_acls_set)
(xattrs_xattrs_get, xattrs_selinux_get, xattrs_selinux_set)
(xattrs_xattrs_set): Prefer bool for booleans.
2024-11-01 23:47:23 -07:00
Paul Eggert
e531f8c66c Prever other types to int in warning.c
* src/warning.c (set_warning_option): Prefer bool for boolean.
2024-11-01 23:47:23 -07:00
Paul Eggert
f4ac66226a Prefer other types to int in transform.c
* src/transform.c (add_char_segment, parse_xform_flags)
(parse_transform_expr): Prefer char for char.
(parse_transform_expr): Don’t assume strlen (expr) <= INT_MAX.
2024-11-01 23:47:23 -07:00
Paul Eggert
6993486ed8 Avoid unlikely verbose_option overflow
* src/tar.c (parse_opt, decode_options):
Avoid undefined behavior if verbose_option overflows.
2024-11-01 23:47:23 -07:00
Paul Eggert
04c1b85872 Prefer other types to int in system.c
* src/system.c (is_regular_file, sys_exec_setmtime_script):
Prefer bool for boolean.
(sys_exec_command): Prefer char for char.
2024-11-01 23:47:23 -07:00
Paul Eggert
ef95115f61 Prefer other types to int in sparse.c
* src/sparse.c (oldgnu_get_sparse_info, star_get_sparse_info):
Prefer char for char.
2024-11-01 23:47:23 -07:00
Paul Eggert
41143ee46f Prefer other types to int in names.c
* src/names.c (uname_to_uid, gname_to_gid, handle_option)
(make_file_name): Prefer bool for boolean.
(struct name_elt, read_name_from_file): Prefer char for char.
(handle_option): Invert sense of return value, for clarity.
All uses changed.
(merge_sort_sll, merge_sort, collect_and_sort_names):
Don’t assume list length fits in int.  Use intptr_t not idx_t,
since the bound is the size of all memory rather than one array.
2024-11-01 23:47:23 -07:00
Paul Eggert
f96aff3ce9 Prefer other types to int in misc.c
* src/misc.c (quote_copy_string, tar_savedir):
Use bool for booleans.  All uses changed.
(quote_copy_string): Use char for chars.
(unquote_string): Return void, since nobody uses return value.
(unquote_string): Check for overflow in escapes like \777.
(wdcache): Now array of idx_t not int, since in theory it
might contain values greater than INT_MAX.  All uses changed.
2024-11-01 23:47:23 -07:00
Paul Eggert
53a3691092 Prefer other types to int in map.c
* src/map.c (map_read): Prefer bool for booleans.
(owner_map_translate, group_map_translate):
Return void, not int, as nobody uses the return value.
2024-11-01 23:47:23 -07:00
Paul Eggert
91ad4ea343 Fix some uses of int in list.c
* src/list.c (decode_xform): Last arg is now int, not a void *
pointer to that int.  All uses changed.
(enforce_one_top_level): Don’t assume string length fits in int.
(transform_stat_info): Prefer char to int for typeflag.
All uses changed.
(decode_header): Prefer bool for booleans.  All uses changed.
(ugswidth): Now idx_t, not int, since in theory it could
exceed INT_MAX.  All uses changed.
(simple_print_header, print_for_mkdir): Don’t assume printf length
fits in int, and similarly for length of user or group name.
* src/transform.c (transform_name_fp): Last arg is now int, not void *.
All uses changed.
2024-11-01 23:47:23 -07:00
Paul Eggert
7eb4dbaff1 Prefer other types to int in incremen.c
* src/incremen.c (struct dumpdir_iter, dumpdir_first)
(read_incr_db_01, dumpdir_ok, list_dumpdir):
Prefer bool to int for booleans.  All uses changed.
(read_incr_db_01): Don’t assume getline returns <= INT_MAX.
(dumpdir_ok): Prefer char to int for chars.
2024-11-01 23:47:23 -07:00
Paul Eggert
112ead7931 Prefer other types to int in extract.c
* src/extract.c (fd_chmod, extract_chdir, open_output_file)
(extract_file, extract_link, extract_symlink, extract_node)
(extract_fifo, tar_extractor_t, pepare_to_extract): Prefer char to
int for typeflag, since it’s a char.  All uses changed.
(fd_chmod): Use clearer code for errno.
(extract_dir, extract_file, create_placeholder_file, extract_link)
(extract_symlink, extract_node, extract_fifo, tar_extractor_t):
Return bool true for success, false for failure.  All uses changed.
(open_output_file): Prefer bool for boolean.
(prepare_to_extract): Simplify by returning the extractor a null
pointer, rather than storing through a pointer to an extractor.
2024-11-01 23:47:23 -07:00
Paul Eggert
fd401e1d29 Prefer other types to int in delete.c
* src/delete.c (write_record): Arg is bool, not int.
All callers changed.
2024-11-01 23:47:23 -07:00
Paul Eggert
f8a679e942 Be a bit more consistent about comparing to zero
* src/buffer.c (xclose, archive_is_dev, close_archive)
(write_fatal_details, init_volume_number)
(closeout_volume_number, new_volume, try_new_volume):
* src/checkpoint.c (format_checkpoint_string):
* src/compare.c (process_rawdata, diff_file, diff_dumpdir):
* src/create.c (create_archive, restore_parent_fd, dump_file0):
* src/delete.c (delete_archive_members):
* src/exclist.c (cvs_addfn):
* src/extract.c (set_mode, mark_after_links, delay_set_stat)
(repair_delayed_set_stat, make_directories, file_newer_p)
(maybe_recoverable, apply_nonancestor_delayed_set_stat)
(extract_dir, open_output_file, find_delayed_link_source)
(create_placeholder_file, extract_symlink, extract_node)
(extract_fifo, apply_delayed_link):
* src/incremen.c (update_parent_directory, scan_directory)
(read_obstack, read_incr_db_2, write_directory_file)
(try_purge_directory):
* src/map.c (map_read):
* src/misc.c (maybe_backup_file, undo_last_backup, chdir_do)
(tar_savedir):
* src/names.c (handle_file_selection_option, add_file_id)
(handle_option, read_next_name, add_hierarchy_to_namelist)
(collect_and_sort_names):
* src/system.c (run_decompress_program, dec_to_env, time_to_env)
(oct_to_env, str_to_env, chr_to_env, sys_exec_setmtime_script):
* src/tar.c (get_date_or_file, parse_default_options)
(decode_options, main):
* src/unlink.c (flush_deferred_unlinks):
* src/update.c (append_file):
* src/xattrs.c (xattrs__acls_set, xattrs_xattrs_set):
Prefer < 0 when looking at syscalls; prefer != 0 to nothing
when testing an integer in a boolean context.
This is for style, not substance; for example, it’s easier
to read ‘if (wordsplit (...) != WRDSE_OK) ...’ than
‘if (wordsplit (...)) ...’ if you don’t already know that
wordsplit returns an enum rather than bool.
* src/names.c (add_file_id, read_next_name, regex_usage_warning):
* src/transform.c (parse_xform_flags):
Return bool not int, possibly inverting sense so that true means OK.
All callers changed.
* src/tar.c (main): Report errno info if stdopen fails.
2024-11-01 23:47:23 -07:00
Paul Eggert
3b0d006830 dumpdir_cmp signature
* src/compare.c (dumpdir_cmp): Return char, not int.
2024-11-01 23:47:23 -07:00
Paul Eggert
6e873de727 Check for checkpoint string overflow
It’s very unlikely, but would lead to undefined behavior.
* src/checkpoint.c (format_checkpoint_string): Accept and return
intmax_t, not idx_t.  All callers changed.  Check for integer
overflow by using add_printf.  If overflow occurs, don’t bother
with extending width.
2024-11-01 23:47:23 -07:00
Paul Eggert
bde3e8d663 Prefer int to idx_t for some small sizes
* src/create.c (max_octal_val, to_octal, tar_copy_str)
(tar_name_copy_str, to_base256, to_chars_subst, to_chars)
(gid_to_chars, major_to_chars, minor_to_chars, mode_to_chars)
(off_to_chars, time_to_chars, uid_to_chars, string_to_chars)
(split_long_name, write_ustar_long_name, simple_finish_header):
* src/list.c (from_header, gid_from_header, major_from_header)
(minor_from_header, mode_from_header, off_from_header)
(time_from_header, uid_from_header):
Prefer int to idx_t where either will do because the buffer sizes
are known to be small, as this can be a performance win on 32-bit
platforms.  Also, in a few cases the values were negative, whereas
idx_t is supposed to be nonnegative.
2024-11-01 23:47:23 -07:00
Paul Eggert
967f5f52f7 Pacify gcc -Wmissing-variable-declarations
* src/buffer.c (start_offset): Now static.
2024-11-01 23:47:23 -07:00
Paul Eggert
5a41310e57 Prefer other types to int in compare.c
* src/compare.c (get_stat_data, verify_volume):
Use bool for booleans.
(verify_volume): Count headers with intmax_t, not int.
2024-11-01 23:47:23 -07:00
Paul Eggert
3357683933 Prefer other types to int in checkpoint.c
* src/checkpoint.c (checkpoint_state): Now enum, not int.
(tty_cleanup): Now bool, not int.
2024-11-01 23:47:23 -07:00
Paul Eggert
a337cd35a0 Prefer other types to int in buffer.c
This increases the volume number maximum from 2**31 - 1	to 2**63 - 1.
* src/buffer.c (record_index, inhibit_map, new_volume):
Prefer bool to int for booleans.
* src/buffer.c (volno, global_volno):
* src/system.c (sys_exec_info_script):
Prefer intmax_t to int.
* src/buffer.c (increase_volume_number): Omit by-hand check for
overflow that relied on undefined behavior.
(new_volume): Check for that overflow here instead, without
relying on undefined behavior.
(print_stats): Avoid undefined behavior if printf sums overflow,
and reliably treat printf error like overflow.
* src/common.h (add_printf): New inline function.
2024-11-01 23:47:23 -07:00
Paul Eggert
5a7185ae31 Prefer other types to int in tar.c
Use types that are more specific than ‘int’, if that is easy.
* src/tar.c (after_date_option, xattrs_option, check_links_option)
(confirm, confirm_file_EOF, set_xattr_option, optloc_eq)
(get_date_or_file):
Prefer bool to int.
(tar_list_quoting_styles, tar_set_quoting_style, parse_opt):
Prefer idx_t to int.
(optloc_lookup, option_set_in_cl): Prefer enum option_class to int.
(decode_signal): Avoid some pointer reallocation.
(sort_mode_flag, hole_detection_types, set_old_files_option)
(is_subcommand_class): Prefer enum to int.
(parse_opt) [DEVICE_PREFIX]: Remove unused var.
Simplify creation of device name.
(find_argp_option_key, find_argp_option): Prefer char to int.
(enum subcommand_class): Now named.
(subcommand_class): Now char, not int.
(decode_options): Check for unlikely int overflow.
2024-11-01 23:47:23 -07:00
Paul Eggert
0aa69501d3 Remove major, minor signedness assumption
* src/common.h (uintmax): Remove; no longer used.
* src/list.c (simple_print_header): Don’t assume major and minor
agree in signedness.
2024-11-01 23:47:23 -07:00
Paul Eggert
2339c9106b Fix checkpoint_flush_actions width typo
* src/checkpoint.c (checkpoint_flush_actions): long → intmax_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
a3ba452f40 Fewer uses of uintmax_t in xheader.c
* src/tar.h (struct xheader):
* src/xheader.c (xheader_string_end):
Use idx_t, not uintmax_t, for string length.
* src/xheader.c (xheader_string_add):
Avoid duplicate calls to strlen.
(xheader_string_end): Remove by-hand check for size overflow;
it’s not possible, as this is measuring allocated storage.
2024-11-01 23:47:23 -07:00
Paul Eggert
d9da938963 Prefer intmax_t for occurrence counts
* src/common.h (struct name):
* src/tar.c (occurrence_option, parse_opt):
Use intmax_t, not uintmax_t, for occurrence counts.
2024-11-01 23:47:23 -07:00
Paul Eggert
989842ff0d Remove unnecessary casts
Some of these date back to pre-C89.
* src/buffer.c (backspace_output):
* src/create.c (to_base256, gid_to_chars, major_to_chars)
(minor_to_chars, off_to_chars, time_to_chars, uid_to_chars):
* src/list.c (from_header, tartime):
* src/map.c (owner_map_translate, group_map_translate):
* src/system.c (sys_truncate):
* src/utf8.c (utf8_init):
* src/xattrs.c (acls_one_line):
* src/xheader.c (xheader_string_end):
Remove casts.
* src/create.c (uintmax_to_chars): Remove.  All uses removed.
(simple_finish_header): Use to_octal.
2024-11-01 23:47:23 -07:00
Paul Eggert
6f5718a35f Check for setenv failures when running scripts
* src/system.c (dec_to_env): Use umaxtostr for speed,
since convenience isn’t needed here.
(sys_exec_info_script, sys_exec_checkpoint_script):
Check for setenv failure.
2024-11-01 23:47:23 -07:00
Paul Eggert
d68c37b640 Prefer off_t to uintmax_t for continued_file_*
* src/buffer.c (continued_file_size, continued_file_offset):
Now off_t, not uintmax_t.  All uses changed.
* src/common.h (UINTMAX_FROM_HEADER):
* src/list.c (uintmax_from_header):
Remove; unused.
* src/list.c (simple_print_header):
* src/xheader.c (volume_size_decoder, volume_offset_decoder):
Treat offset as off_t, not uintmax_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
c0ef66da92 Prefer idx_t to size_t in common.h
* src/common.h (struct name): Prefer idx_t to size_t.
(volume_label_count): Remove; unused.
2024-11-01 23:47:23 -07:00
Paul Eggert
c2ce0b7e13 Prefer idx_t to size_t in tar.h
* src/tar.h (struct xheader, struct tar_stat_info):
Prefer idx_t to size_t.  All uses changed.
2024-11-01 23:47:23 -07:00
Paul Eggert
7b278044a7 Prefer idx_t to size_t in xheader.c
* src/xheader.c (x_obstack_grow, x_obstack_blank)
(xheader_format_name, xheader_ghdr_name, xheader_write)
(struct xhdr_tab, locate_handler, decode_record, decx, decg)
(xheader_read, xattr_encode_keyword, xheader_print_n)
(xheader_string_end, dummy_decoder, atime_decoder, gid_decoder)
(gname_decoder, linkpath_decoder, ctime_decoder, mtime_decoder)
(path_decoder, sparse_path_decoder, size_decoder, uid_decoder)
(uname_decoder, sparse_size_decoder, sparse_numblocks_decoder)
(sparse_offset_coder, sparse_offset_decoder)
(sparse_numbytes_coder, sparse_numbytes_decoder)
(sparse_map_decoder, dumpdir_decoder, volume_label_decoder)
(volume_size_decoder, volume_offset_decoder)
(volume_filename_decoder, xattr_selinux_decoder)
(xattr_acls_a_decoder, xattr_acls_d_decoder, xattr_coder)
(xattr_decoder, sparse_major_decoder, sparse_minor_decoder):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
025f19e6bd Prefer intmax_t to size_t in xheader.c
* src/common.h (INTMAX_STRSIZE_BOUND): New constant.
(SYSINT_BUFSIZE): Use it.
* src/xheader.c (global_header_count, xheader_format_name):
Prefer intmax_t to size_t, as the values are not sizes.
2024-11-01 23:47:23 -07:00
Paul Eggert
c61a2bee73 Omit unnecessary initialization in dunlink_alloc
* src/unlink.c (dunlink_alloc): Remove unnecessary assignment
to p->next.
2024-11-01 23:47:23 -07:00
Paul Eggert
08a9174444 Remove unused static vars in unlink.c
* src/unlink.c (unlink_count, deferred_unlink_delay):
Remove.  All uses removed.
2024-11-01 23:47:23 -07:00
Paul Eggert
e0f9b0fdea Prefer idx_t to size_t in transform.c
* src/transform.c (struct replace_segm, struct transform)
(add_literal_segment, add_backref_segment, run_case_conv)
(_single_transform_name_to_obstack): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
17ad155fb2 Prefer idx_t to size_t in xattrs.c
* src/xattrs.c (xattr_map_free, xattr_map_add)
(xheader_xattr_add, xattr_map_copy, struct xattrs_mask_map)
(fixup_extra_acl_fields, xattrs_acls_cleanup, acls_get_text)
(xattrs__acls_get_a, xattrs__acls_get_d, acls_one_line)
(mask_map_realloc, xattrs_xattrs_get, xattrs__fd_set)
(xattrs_matches_mask, xattrs_xattrs_set, xattrs_print_char)
(xattrs_print): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
303ac16ec0 Prefer idx_t to size_t in tar.c
* src/tar.c (strip_name_components, archive_names)
(allocated_archive_names, tar_list_quoting_styles)
(expand_pax_option, parse_opt):
Prefer idx_t to size_t.
(decode_options): Use a static word rather than going
to to the bother of dynamically allocating an array.
(main): Do not preallocate array.  Do not call ‘free’
on a pointer that now might be to static storage.
2024-11-01 23:47:23 -07:00
Paul Eggert
6df7a72434 Prefer idx_t to size_t in system.c
* src/buffer.c (_flush_write): Return idx_t, not ssize_t,
to accommodate system.c changes.  All uses changed.
(_gnu_flush_write): Output correct errno value after write error.
Simplify multi-volume mode.
* src/system.c (sys_write_archive_buffer)
(sys_child_open_for_compress, sys_exec_setmtime_script):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
23582f3445 Streamline compression suffix detection
* src/suffix.c (struct compression_suffix):
Use arrays rather than pointers that need relocation.
All uses changed.
(compression_suffixes): Now const.
Omit trailing null entry; all uses changed.
(find_compression_suffix): Simplify length calculations.
No longer any need to call strlen.
2024-11-01 23:47:23 -07:00
Paul Eggert
317e4d6a3c Fewer uses of size_t in suffix.c
* src/suffix.c (struct compression_suffix)
(find_compression_suffix, set_compression_program_by_suffix)
(strip_compression_suffix): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
5f4a4164b7 Fewer uses of size_t in sparse.c
* src/sparse.c (struct tar_sparse_optab, dump_zeros)
(tar_sparse_dump_region, tar_sparse_extract_region)
(zero_block_p, sparse_add_map, sparse_dump_region)
(sparse_extract_region, sparse_dump_file, sparse_extract_file)
(check_data_region, sparse_diff_file, oldgnu_get_sparse_info)
(oldgnu_store_sparse_info, oldgnu_dump_header)
(star_get_sparse_info, pax_dump_header_0):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
7c0feaefd0 Adjust better to Gnulib signed-int read changes
The 2024-08-09 Gnulib changes that caused some modules prefer
signed types to size_t means that Tar should follow suit.
* src/buffer.c (short_read):
* src/system.c (sys_child_open_for_compress)
(sys_child_open_for_uncompress):
rmtread and safe_read return ptrdiff_t not idx_t;
don’t rely on implementation defined conversion.
* src/misc.c (blocking_read): Never return a negative number.
Return idx_t, not ptrdiff_t, with the same convention for EOF
and error as the new full_read.  All callers changed.
* src/sparse.c (sparse_dump_region, check_sparse_region)
(check_data_region):
* src/update.c (append_file):
full_read no longer returns SAFE_READ_ERROR for I/O error; instead it
returns the number of bytes successfully read, and sets errno.
Adjust to this.
* src/system.c (sys_child_open_for_uncompress):
Rewrite to avoid need for goto and label.
2024-11-01 23:47:23 -07:00
Paul Eggert
e513950080 Simplify name_buffer initialization
* src/names.c (name_init): Remove no-longer-needed initialization
of name_buffer, name_buffer_length.  It was confusing anyway,
since it caused name_buffer_length to not equal the length of
name_buffer.
2024-11-01 23:47:23 -07:00
Paul Eggert
2ce5791124 Simplify add_hierarchy_to_namelist allocation
* src/names.c (add_hierarchy_to_namelist):
Use xpalloc rather than a complicated homebrew heuristic.
2024-11-01 23:47:23 -07:00
Paul Eggert
d127dac10e Remove xattrs_clear_setup
It’s never actually called.
* src/xattrs.c (clear_mask_map, xattrs_clear_setup):
Remove.  All uses removed.
2024-11-01 23:47:23 -07:00
Paul Eggert
61a978f6d4 Remove name_term
It’s never actually called.
* src/names.c (name_term): Remove.  All uses removed.
2024-11-01 23:47:23 -07:00
Paul Eggert
fae968bd2d Diagnose sys_exec_info_script failures
* src/system.c (sys_exec_info_script): Diagnose failures in
getline, fclose.  Don’t worry about freeing memory
as caller will immediately exit anyway.
2024-11-01 23:47:23 -07:00
Paul Eggert
9afbe6961c Pacify GCC in info_attach_exclist
* src/exclist.c (info_attach_exclist): Remove unnecessary test
for whether dir and ex are null.  GCC complains about the first
one in some cases.  Use C99-style decls.
2024-11-01 23:47:23 -07:00
Paul Eggert
5704e5795a Fewer uses of size_t in names.c
* src/names.c (name_buffer_length, read_name_from_file)
(copy_name, all_names_found, add_hierarchy_to_namelist)
(rebase_child_list, make_file_name, stripped_prefix_len):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
b73127edc4 Fewer uses of size_t in misc.c
* src/misc.c (assign_string_n, quote_copy_string)
(normalize_filename, replace_prefix, remove_any_file)
(blocking_read, wd_alloc, wdcache_count, chdir_arg, chdir_do)
(read_diag_details, struct namebuf, namebuf_name):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
dd71d3796d Fewer uses of size_t in list.c
* src/list.c (recent_long_name_blocks, recent_long_link_blocks)
(read_header, from_header, gid_from_header, major_from_header)
(minor_from_header, mode_from_header, off_from_header)
(time_from_header, uid_from_header, uintmax_from_header)
(tartime): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
f73c927a71 Fewer uses of size_t in incremen.c
* src/incremen.c (struct dumpdir, dumpdir_create0, struct dumpdir_iter)
(dumpdir_next, dumpdir_size, make_directory)
(dirlist_replace_prefix, rebase_directory, makedumpdir)
(maketagdumpdir, append_incremental_renames, read_obstack)
(read_incr_db_2, get_gnu_dumpdir, try_purge_directory)
(list_dumpdir): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
04b92eca49 Fewer uses of size_t in extract.c
* src/extract.c (struct delayed_set_stat, struct delayed_link)
(delay_set_stat, apply_nonancestor_delayed_set_stat)
(extract_file): Prefer idx_t to size_t.
(struct delayed_set_stat): Remove unused member xattr_map_size.
2024-11-01 23:47:23 -07:00
Paul Eggert
5a00343006 Fewer uses of size_t in exclist.c
* src/exclist.c (hg_addfn): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
739483114d Fewer uses of size_t in delete.c
* src/delete.c (write_recent_blocks, write_recent_bytes):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
849f244a0b Fewer uses of size_t in create.c
* src/create.c (struct exclusion_tag, to_octal, tar_copy_str)
(tar_name_copy_str, to_base256, to_chars_subst, to_chars)
(gid_to_chars, major_to_chars, minor_to_chars, mode_to_chars)
(off_to_chars, time_to_chars, uid_to_chars, uintmax_to_chars)
(string_to_chars, start_private_header, write_gnu_long_link)
(split_long_name, write_ustar_long_name, simple_finish_header)
(dump_dir0, ensure_slash, create_archive):
Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
15dc3210cc Fewer uses of size_t in compare.c
* src/compare.c (read_and_process): Prefer idx_t to size_t.
2024-11-01 23:47:23 -07:00
Paul Eggert
7abf1420c3 Simplify checkpoint_action allocation
* src/checkpoint.c: Include <flexmember.h>.
(struct checkpoint_action): New member commandbuf.
(checkpoint_action_tail): Now pointer to pointer,
to simplify updating.  All uses changed.
(alloc_action): New arg quoted_string, to lessen number of
separate allocations.  All uses changed.
2024-11-01 23:47:23 -07:00
Paul Eggert
0b60228081 checkpoint_total_format is now const
* src/checkpoint.c (checkpoint_total_format):
Now const, and local to format_checkpoint_string.
2024-11-01 23:47:23 -07:00
Paul Eggert
f9ed22de9b Fewer uses of size_t in checkpoint.c
* src/checkpoint.c (copy_string_unquote, getarg)
(format_checkpoint_string): Prefer idx_t to size_t.
(copy_string_unquote): Simplify by using ximemdup0.
(getarg): Avoid quadratic reallocation behavior by
using xpalloc.
2024-11-01 23:47:23 -07:00
Paul Eggert
78dd7bf0bc Fewer uses of size_t in buffer.c
* src/buffer.c (flush_write_ptr, flush_bufmap, bufmap_locate):
(struct zip_magic, available_space_after, _flush_write)
(short_read, flush_archive, try_new_volume)
(gnu_add_multi_volume_header, simple_flush_read)
(simple_flush_write, _gnu_flush_read, _gnu_flush_write)
(gnu_flush_write): Prefer idx_t to size_t when either will do, as
signed types are typically safer.  For a tiny value in memory,
just use ‘char’.
2024-11-01 23:47:23 -07:00
Sergey Poznyakoff
647cafff96 Don't assume archive read from stdin starts at offset 0
* src/buffer.c (start_offset): New variable.
(get_archive_status): If reading from seekable stdin, store the
position in the stream corresponding to record_start in start_offset.
(seek_archive): Compute current offset relative to start_offset.
2024-10-31 19:17:05 +02:00
Sergey Poznyakoff
bd06b114d9 Add missing safety check
* src/system.c (sys_exec_info_script): Check if fdopen succeded.
Fix by Matteo Croce.
2024-10-25 13:40:32 +03:00
Sergey Poznyakoff
8767b1c84a Remove useless check
* src/exclist.c (info_attach_exclist): Don't check whether dir
is NULL. It can't be.
2024-10-25 10:24:32 +03:00
Sergey Poznyakoff
e59d09db7d Bugfix
This fixes an extra argument left over in a function call by commit
0dfcfa4aa4.  Reported by Matteo Croce.

* src/buffer.c (_open_archive): Fix extra argument to paxfatal.
2024-10-25 09:50:22 +03:00
Paul Eggert
dd1bae32ce Fewer macros in xheader.c
* src/xheader.c (HEADER_TEMPLATE):
Remove.  All uses replaced with definiens.
(XHDR_PROTECTED, XHDR_GLOBAL): Now constants, not macros.
2024-08-19 09:57:13 -07:00
Paul Eggert
8b3073e1d2 Fewer macros in xattrs.c
* src/xattrs.c (XATTRS_PREFIX, XATTRS_PREFIX_LEN, USER_DOT_PFX):
Now constants, not macros.
2024-08-19 09:57:13 -07:00
Paul Eggert
7c4f884747 Fewer macros in unlink.c
* src/unlink.c (IS_CWD): Now a (lower-cased) function.
2024-08-19 09:57:13 -07:00
Paul Eggert
82ef07c9bd Fewer macros in transform.c
* src/transform.c (CASE_CTL_RESET): Remove, replacing with one
instance of code (with a goto, alas).  Still a bit clearer, I think.
2024-08-19 09:57:13 -07:00
Paul Eggert
350cc4077e Fewer macros in tar.c
* src/tar.c (FORMAT_MASK, TAR_SIZE_SUFFIXES, SUBCL_READ)
(SUBCL_WRITE, SUBCL_UPDATE, SUBCL_TEST, SUBCL_OCCUR)
(IS_SUBCOMMAND_CLASS, NS_PRECISION_FORMAT_MASK):
Now constants or (lower-cased) functions, not macros.
(subcommand_class):
Replace hopeful comments with code implementing them.
2024-08-19 09:57:13 -07:00
Paul Eggert
7f557428a4 Fewer macros in tar.h
* src/tar.h (REGTYPE, AREGTYPE, SYMTYPE, BLKTYPE, FIFOTYPE)
(XHDTYPE, XGLTYPE, TSUID, TSGID, TSVTX, TUREAD, TUWRITE, TUEXEC)
(TGREAD, TGWRITE, TGEXEC, TOREAD, TOWRITE, TOEXEC)
(SPARSES_IN_EXTRA_HEADER, SPARSES_IN_OLDGNU_HEADER)
(SPARSES_IN_SPARSE_HEADER, GNUTYPE_DUMPDIR, GNUTYPE_LONGLINK)
(GNUTYPE_LONGNAME, GNUTYPE_MULTIVOL, GNUTYPE_SPARSE)
(GNUTYPE_VOLHDR, SOLARIS_XHDTYPE, SPARSES_IN_STAR_HEADER)
(SPARSES_IN_STAR_EXT_HEADER, BLOCKSIZE):
Now constants, not macros.
2024-08-19 09:57:13 -07:00
Paul Eggert
dd0f95965d Fewer macros in system.c
* src/system.c (PREAD, PWRITE): Now constants, not macros.
2024-08-19 09:57:13 -07:00
Paul Eggert
cdcd1580c8 Fewer macros in names.c
* src/names.c (EXCLUDE_OPTIONS, INCLUDE_OPTIONS):
Now (lowercased) functions, not macros.
(SUCCESSOR): Remove, replacing uses with definiens.
2024-08-19 09:57:13 -07:00
Paul Eggert
dfb1da7253 Fewer macros in incremen.c
* src/incremen.c (DIRF_INIT, DIRF_NFS, DIRF_FOUND, DIRF_NEW)
(DIRF_RENAMED, DIR_IS_INITED, DIR_IS_NFS, DIR_IS_FOUND)
(DIR_IS_RENAMED, DIR_SET_FLAG, DIR_CLEAR_FLAG, NFS_FILE_STAT)
(PD_FORCE_CHILDREN, PD_FORCE_INIT, PD_CHILDREN)
(TAR_INCREMENTAL_VERSION, TEMP_DIR_TEMPLATE):
Now constants or (lowercased) functions, not macros.
(ST_DEV_MSB) [!HAVE_ST_FSTYPE_STRING]: Remove.
Replace only use with something simpler.
2024-08-19 09:57:13 -07:00
Paul Eggert
79cb9aaab6 Fewer macros in extract.c
* src/extract.c (ALL_MODE_BITS, RECOVER_NO, RECOVER_OK)
(RECOVER_SKIP): Now constants or inline functions, not macros.
(maybe_recoverable): Return enum recover, not int.
2024-08-19 09:57:13 -07:00
Paul Eggert
da109fae7a Fewer macros in create.c
* src/create.c (CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE)
(MAX_VAL_WITH_DIGITS, MAX_OCTAL_VAL): Now constants or
inline functions or removed, instead of macros.
(max_octal_val): Accept size rather than type.
2024-08-19 09:57:13 -07:00
Paul Eggert
cc1352699a Fewer macros in buffer.c
* src/buffer.c (READ_ERROR_MAX, NMAGIC, VOL_SUFFIX):
Now constants rather than macros.  Rename NMAGIC to n_zip_magic.
2024-08-19 09:57:13 -07:00
Paul Eggert
4323e98683 Fewer macros in common.h
In common.h, replace macros with constants or functions when that
is easy.  This makes code a bit more reliable (functions evaluate
their args exactly once) and easier to debug (many debugging
environments cannot access macros).
* src/common.h (CHKBLANKS): Remove.  All uses removed.
(NAME_FIELD_SIZE, PREFIX_FIELD_SIZE, UNAME_FIELD_SIZE)
(GNAME_FIELD_SIZE, TAREXIT_SUCCESS, TAREXIT_DIFFERS)
(TAREXIT_FAILURE, LG_8, LG_256, DEFAULT_CHECKPOINT)
(MAX_OLD_FILES, TF_READ, TF_WRITE, TF_DELETED, XFORM_REGFILE)
(XFORM_LINK, XFORM_SYMLINK, XFORM_ALL, WARN_ALONE_ZERO_BLOCK)
(WARN_BAD_DUMPDIR, WARN_CACHEDIR, WARN_CONTIGUOUS_CAST)
(WARN_FILE_CHANGED, WARN_FILE_IGNORED, WARN_FILE_REMOVED)
(WARN_FILE_SHRANK, WARN_FILE_UNCHANGED, WARN_FILENAME_WITH_NULS)
(WARN_IGNORE_ARCHIVE, WARN_IGNORE_NEWER, WARN_NEW_DIRECTORY)
(WARN_RENAME_DIRECTORY, WARN_SYMLINK_CAST, WARN_TIMESTAMP)
(WARN_UNKNOWN_CAST, WARN_UNKNOWN_KEYWORD, WARN_XDEV)
(WARN_DECOMPRESS_PROGRAM, WARN_EXISTING_FILE, WARN_XATTR_WRITE)
(WARN_RECORD_SIZE, WARN_FAILED_READ, WARN_MISSING_ZERO_BLOCKS)
(WARN_VERBOSE_WARNINGS, WARN_ALL, EXCL_DEFAULT, EXCL_RECURSIVE)
(EXCL_NON_RECURSIVE): Now enum constants rather than macros.
(time_option_initialized, isfound, wasfound, warning_enabled):
Now functions rather than macros TIME_OPTION_INITIALIZED, ISFOUND,
WASFOUND, WARNING_ENABLED.  All uses changed.
(OLDER_STAT_TIME, OLDER_TAR_STAT_TIME, EXTRACT_OVER_PIPE)
(TAR_ARGS_INITIALIZER): Remove.  All uses replaced with their
definiens or equivalent.
2024-08-19 09:57:13 -07:00
Paul Eggert
005e345c04 Fix non-ASCII in sparse.c 2024-08-19 09:57:13 -07:00
Paul Eggert
95a5f043c5 Prefer function to COPY_BUF macro
* src/sparse.c (struct ok_n_block_ptr): New type.
(decode_num): Revamp API so that it does the work of both
the old decode_num and the old COPY_BUF.  Always read to the
next newline even if there is a lot of junk in between.
(pax_decode_header): Use the new API.
(COPY_BUF): Remove.
2024-08-19 09:57:13 -07:00
Paul Eggert
f25dd56e83 Prefer function to COPY_STRING macro
* src/sparse.c (struct block_ptr):
New type, to allow a functional style.
(dump_str_nl, floorlog10): New static functions.
(COPY_STRING): Remove.  All uses replaced by dump_str_nl.
(pax_dump_header_1): Use floorlog10 instead of creating a string.
Simplify size calculation.
2024-08-19 09:57:13 -07:00
Paul Eggert
f1e4947992 Fix string size bound calculation
* src/common.h (UINTMAX_STRSIZE_BOUND):
Fix typo that luckily didn’t break anything.
2024-08-19 09:57:13 -07:00
Paul Eggert
0dfcfa4aa4 maint: switch from ERROR to paxerror etc
Prefer functions like ‘paxerror’ to macros like ‘ERROR’.
The functions have cleaner semantics, and calls are
easier to read.
2024-08-19 09:57:13 -07:00
Paul Eggert
e9c16628f0 build: update gnulib and paxutils submodules to latest 2024-08-19 09:57:13 -07:00
Paul Eggert
a0a1243c69 Adjust to verror change for program name
* configure.ac (ENABLE_ERROR_PRINT_PROGNAME):
Adjust to match new Gnulib behavior.
2024-08-15 10:27:47 -07:00
Paul Eggert
812a49419a build: update gnulib and paxutils submodules to latest 2024-08-14 23:25:46 -07:00
Paul Eggert
541f3bc374 Fix duplicate write_error_details decl
* src/common.h (write_error_details): Remove decl
that belongs to paxutils.
2024-08-14 23:25:46 -07:00
Paul Eggert
6bc4c4bf96 Fix minor diagnostic discrepancies in incrementals
* src/incremen.c (read_directory_file, get_gnu_dumpdir):
Be more consistent about fatal errors.
2024-08-14 23:25:46 -07:00
Paul Eggert
ab7a14bd92 Add verror module
* gnulib.modules: Add verror; paxlib will need this.
2024-08-14 23:25:46 -07:00
Paul Eggert
b596676c78 Use idx_t for write_fatal_details size
* src/buffer.c (write_fatal_details): Prefer idx_t to size_t
for object size.
2024-08-14 23:25:46 -07:00
Paul Eggert
15c6010c32 Use intmax_t for read_incr_db_01 line numbers
* src/incremen.c (read_incr_db_01): Don’t assume line numbers
are less than LONG_MAX.
2024-08-14 23:25:46 -07:00
Paul Eggert
43231ae554 Avoid need for base64_init and extra table
Simplify the code by assuming C99 initializers.
* src/list.c (base_64_digits): Remove.
(base64_map): Now a constant.  Now has its (old value + 1) % 65,
as that’s the only easy portable way to do it with a static
initializer (even on platforms where CHAR_BIT != 8); all uses changed.
(base64_init): Remove; only use removed.
(from_header): Adjust to new values in base64_map.

* src/list.c (base_64_digits): Remove; no longer needed.
(base64_map): Now const, initialized statically, and with
invalid entries being 0 not 64, and with valid entries
being 1 greater than before.
2024-08-14 23:25:46 -07:00
Paul Eggert
b201a37421 Remove cast from from_header
* src/list.c (from_header): Reword to avoid a cast
to unsigned char.
2024-08-14 23:25:46 -07:00
Paul Eggert
c9a3abcbe7 Prefer signed to unsigned when decoding options
* src/tar.c (assert_format, decode_options):
Prefer signed to unsigned integers.
(optloc_save): Prefer enum to unsigned integer.
Simplify allocation.
(decode_options): No need to call ngettext for a value known
to be plenty large.
2024-08-14 23:25:46 -07:00
Paul Eggert
18dadeffc0 Don’t assume pid fits in unsigned long
* src/system.c (sys_wait_command): Convert pid_t to intmax_t,
not to unsigned long.
2024-08-14 23:25:46 -07:00
Paul Eggert
1521d3dae0 Avoid casts in tar_checksum
* src/list.c (tar_checksum, from_header):
Recode to avoid casts.
2024-08-14 23:25:46 -07:00
Paul Eggert
5ab90d6c96 Support >UINT_MAX lines in map files
* src/map.c (parse_id, map_read): Prefer intmax_t to unsigned
for line numbers.
2024-08-14 23:25:46 -07:00
Paul Eggert
e137c14285 Prefer signed integer in struct directory
* src/incremen.c (struct directory):
Prefer int to unsigned where either will do.
2024-08-14 23:25:46 -07:00
Paul Eggert
95ebde4303 Simplify make_directory via xizalloc
* src/incremen.c (make_directory): Simplify by using
xizalloc instead of explicit initialization.
2024-08-14 23:25:46 -07:00
Paul Eggert
ef290cb171 Use idx_t, not size_t, for xattr value lengths.
* src/tar.h (struct xattr_map):
* src/xattrs.c (xattr_map_add): Prefer idx_t to size_t.  All uses
changed.
2024-08-14 23:25:46 -07:00
Paul Eggert
09aec02e32 Use intmax_t, not size_t, for input line numbers
This works better on platforms where SIZE_MAX < OFF_MAX.
* src/common.h (struct common locus):
* src/names.c (struct name_elt):
Use intmax_t for line numbers.  All uses changed.
2024-08-14 23:25:46 -07:00
Paul Eggert
9b69d17e24 In short_read, use %td not %lu
* src/buffer.c (short_read): Don’t assume sizes fit
in unsigned long.
2024-08-14 23:25:46 -07:00
Paul Eggert
b3992e4ef8 Prefer signed types in blocking_read etc
* src/compare.c (process_noop, process_rawdata):
Return bool, not int.
* src/compare.c (process_noop, process_rawdata):
* src/create.c (dump_regular_file):
* src/extract.c (extract_file):
* src/misc.c (blocking_read, blocking_write):
* src/sparse.c (sparse_scan_file_raw, sparse_extract_region):
Prefer signed types like idx_t to unsigned ones like size_t.
(sparse_scan_file_raw): Diagnose read errors.
2024-08-14 23:25:46 -07:00
Paul Eggert
88c2aa1616 Fix minor integer overflow in xsparse.c
* scripts/xsparse.c (read_xheader):
Don’t assume size_t fits in unsigned.
Make the version numbers off_t, not just unsigned.
2024-08-14 23:25:46 -07:00
Paul Eggert
d1e72a536f Prefer stoint to strtoul and variants
When parsing numbers prefer using strtosysint (renamed stoint)
to using strtoul and its variants.
This is simpler and faster and likely more reliable than
relying on quirks of the system strtoul etc,
and it standardizes how tar deals with parsing integers.
Among other things, the C standard and POSIX don’t specify
what strtol does to errno when conversions cannot be performed,
and it requires strtoul to support "-" before unsigned numbers.
* gnulib.modules (strtoimax, strtol, strtoumax, xstrtoimax):
Remove.
* src/checkpoint.c (checkpoint_compile_action, getwidth)
(format_checkpoint_string):
* src/incremen.c (read_incr_db_01, read_num)
* src/map.c (parse_id):
* src/misc.c (decode_timespec):
* src/sparse.c (decode_num):
* src/tar.c (parse_owner_group, parse_opt):
* src/transform.c (parse_transform_expr):
* src/xheader.c (decode_record, decode_signed_num)
(sparse_map_decoder):
Prefer stoint to strtol etc.
Don’t rely on errno == EINVAL as the standards don’t guarantee it.
* src/checkpoint.c (getwidth, format_checkpoint_string):
Check for invalid string suffix.
* src/checkpoint.c (getwidth):
Return intmax_t, not long.  All callers changed.
* src/incremen.c (read_directory_file):
It’s just a one-digit number, so just subtract '0'.
* src/map.c (parse_id): Return bool not int.  All callers changed.
* src/misc.c (stoint): Rename from strtosysint, and add
a bool * argument for reporting overflow.  All callers changed.
(decode_timespec): Simplify by using ckd_sub rather than
checking for overflow by hand.
* src/tar.c (incremental_level): Now signed char to
emphasize that it can be only -1, 0, 1.  All uses changed.
* src/xheader.c (decode_record): Avoid giant diagnostics.
2024-08-14 23:25:46 -07:00
Paul Eggert
3ffe2eb073 Handle enormous record sizes better
Formerly the code could misbehave when the user specified a record
size greater than min (INT_MAX * 512 + 511, PTRDIFF_MAX, SSIZE_MAX).
* src/delete.c (new_blocks, delete_archive_members):
* src/system.c (sys_exec_info_script):
* src/tar.c (blocking_factor, record_size):
Don’t limit blocking factor to INT_MAX.
Prefer signed type for record_size.
Do not exceed IDX_MAX or SSIZE_MAX for record_size;
the SSIZE_MAX limit is needed so that ‘read’ and ‘write’
calls behave sensibly.
2024-08-14 23:25:46 -07:00
Paul Eggert
eb9bb9bf80 Default to GNU/Linux dev_t etc
* configure.ac (dev_t, ino_t, major_t, minor_t):
Default to GNU/Linux types.  This shouldn’t affect behavior;
it’s just a cleanup.
2024-08-14 23:25:45 -07:00
Paul Eggert
4642cd04ed Avoid strtoul
This is part of the general trend to prefer signed integer types,
to allow better runtime checking with -fsanitize=undefined etc.
* gnulib.modules: Remove strtoul.  Add xstrtoimax.
* src/checkpoint.c (checkpoint, format_checkpoint_string):
* src/system.c (sys_exec_checkpoint_script):
* src/tar.c (checkpoint_option):
Use intmax_t, not unsigned, for checkpoint numbers.
All uses changed.
* src/checkpoint.c (checkpoint_compile_action): Don’t assume
time_t == unsigned long.  Treat overflows as TYPE_MAXIMUM (time_t),
essentially infinity.
* src/tar.c (tar_sparse_major, tar_sparse_minor):
* src/tar.h (struct tar_stat_info):
Use intmax_t, not unsigned, for sparse major and minor.
All uses changed.
* src/tar.c (parse_opt):
Don’t mishandle multiple specifications of sparse major and minor.
* src/transform.c (struct transform):
Use idx_t, not unsigned, for match_number.  All uses changed.
(parse_transform_expr): Don’t mishandle large match numbers
by wrapping them around.
2024-08-14 23:25:45 -07:00
Paul Eggert
a80f364662 Avoid snprintf
* gnulib.modules: Remove snprintf.
* lib/wordsplit.c (wordsplit_pathexpand):
Do not arbitrarily truncate diagnostic.
(wordsplit_c_quote_copy): Rewrite to avoid the need to
invoke snprintf on a temporary buffer.
2024-08-04 01:41:43 -07:00
Paul Eggert
5316938142 Avoid wordsplit quadratic behavior
* lib/wordsplit.c (wsplt_assign_var):
Avoid unlikely overflow when adding wsp->ws_envidx + n.
Avoid quadratic	behavior when growing ws_envbuf.
2024-08-04 01:41:43 -07:00
Paul Eggert
83926613a4 Prefer ialloc for wordsplit
* lib/wordsplit.c (alloc_space, wsplt_assign_var, expvar)
(wordsplit_tildexpand, wordsplit_pathexpand)
(wordsplit_get_words): Use ialloc API on idx_t args.
2024-08-04 01:41:43 -07:00
Paul Eggert
9a2344b183 Omit wordsplit API that tar doesn’t need
* lib/wordsplit.c: Include <attribute.h> here, not in wordsplit.h.
(WRDSO_ESC_SET, WRDSO_ESC_TEST): Move here from wordsplit.h.
(WORDSPLIT_EXTRAS_extern): New macro.  Used by functions
that tar doesn’t need to be exposed.
(wordsplit_append, wordsplit_c_quoted_length, wsplt_quote_char)
(wordsplit_c_unquote_char, wordsplit_c_quote_char)
(wordsplit_c_quote_copy, wordsplit_get_words, wordsplit_perror):
Omit unless _WORDSPLIT_EXTRAS.
(WORDSPLIT_ENV_INIT): Move here from wordsplit.h, and
make it a constant rather than a macro.
(wordsplit_strerror): Arg is now pointer to const.
* lib/wordsplit.h: Do not include attribute.h, so that library
users need not worry about attribute.h.
(wordsplit_t): Declare only if _WORDSPLIT_EXTRAS.  Similarly for
functions that are not exported to tar.
2024-08-04 01:41:43 -07:00
Paul Eggert
5182462cf1 wordsplit_get_words need not fail
* lib/wordsplit.c (wordsplit_get_words):
Do not fail merely because realloc fails.
Return void, since failure is no longer possible.
2024-08-04 01:41:43 -07:00
Paul Eggert
0ab451a420 More wordsplit int cleanup
* lib/wordsplit.c: Include limits.h.
(_wsplt_subsplit, wordsplit_add_segm, wsnode_quoteremoval)
(wsnode_coalesce, wsnode_tail_coalesce, find_closing_paren)
(expvar, begin_var_p, node_expand, begin_cmd_p, expcmd)
(scan_qstring, scan_word, wordsplit_c_quoted_length)
(wordsplit_string_unquote_copy, wordsplit_c_quote_copy)
(exptab_matches, wordsplit_process_list):
Prefer bool to int.
(wordsplit_init, alloc_space, coalesce_segment)
(wsnode_quoteremoval, wordsplit_finish, wordsplit_append):
Use WRDSE_OK instead of 0 when the context is that of WRDSE_*.
(wsnode_flagstr, coalesce_segment, wsnode_quoteremoval)
(wordsplit_finish, node_split_prefix, wsplt_assign_var, expvar)
(expcmd, wordsplit_tildexpand, wordsplit_pathexpand)
(wsplt_unquote_char, wsplt_quote_char)
(wordsplit_string_unquote_copy):
Prefer '\0' to 0 when it is a char.
(wsnode_insert): Omit last arg, which was always 0.
All callers changed.
(wordsplit_add_segm, node_split_prefix):
Use unsigned, not int, for flag, for consistency.
(wordsplit_finish, begin_var_p, begin_cmd_p, skip_sed_expr)
(xtonum, wsplt_unquote_char, wsplt_quote_char)
(wordsplit_c_unquote_char, wordsplit_c_quote_char)
(wordsplit_c_quote_copy):
Prefer char to int for chars.
(xtonum): Don’t treat "\400" as if it were "\000".
2024-08-04 01:41:43 -07:00
Paul Eggert
dab2830e38 Diagnose argp overflow
* src/names.c (handle_option):
* src/tar.c (parse_default_options):
Report an error if wordsplitting yields more than INT_MAX words,
rather than misbehaving.  argp_parse can’t handle more than
INT_MAX, unfortunately.
2024-08-04 01:41:43 -07:00
Paul Eggert
9cef4d5495 Fix unlikely buffer overrun when checkpointing
* src/checkpoint.c (format_checkpoint_string):
Don’t overrun buffer when word splitting.
2024-08-04 01:41:43 -07:00
Paul Eggert
7cda31b1e0 Prefer idx_t to size_t in wordsplit
* gnulib.modules: Add ialloc.
* lib/wordsplit.c: Include ialloc.h.
(PRINTMAX): New constant.
(printflen, printfdots): New functions.
(wordsplit_dump_nodes, expvar, wordsplit_process_list): Use them.
(_wsplt_subsplit, wordsplit_string_unquote_copy):
Don’t limit lengths to INT_MAX.
(wordsplit_run): Remove.  All callers changed to use wordsplit_len.
(wordsplit_perror): Don’t limit lengths to ULONG_MAX.
* lib/wordsplit.c (wordsplit_init, alloc_space, struct wordsplit_node)
(wsnode_len, wordsplit_add_segm, coalesce_segment, wsnode_quoteremoval)
(wordsplit_finish, wordsplit_append, node_split_prefix)
(find_closing_paren, wordsplit_find_env, wsplt_assign_var)
(expvar, node_expand, expcmd, wordsplit_trimws)
(wordsplit_tildexpand, isglob, wordsplit_pathexpand)
(skip_sed_expr, skip_delim_internal, skip_delim)
(skip_delim_real, scan_qstring, scan_word)
(wordsplit_c_quoted_length, wordsplit_process_list)
(wordsplit_len, wordsplit_free_words, wordsplit_get_words):
* lib/wordsplit.h (struct wordsplit):
Prefer idx_t to size_t for indexes.
* lib/wordsplit.h: Include idx.h.
2024-08-04 01:41:43 -07:00
Paul Eggert
cc691f8272 Support >INT_MAX -C dirs
* src/extract.c (struct delayed_set_stat, struct delayed_link):
* src/misc.c (normalize_filename, wd_count, chdir_count)
(chdir_arg, tar_getcdpath):
* src/names.c (name_gather, addname, add_hierarchy_to_namelist):
* src/unlink.c (struct deferred_unlink, flush_deferred_unlinks):
Use idx_t, not int, for directory indexes, so as to not
limit their number to INT_MAX; this is theoretically possible
if -T is used.
* src/names.c (name_next_elt, name_next):
Use bool for boolean.
2024-08-04 01:41:43 -07:00
Paul Eggert
390950282d maint: fix some encodings and email addresses 2024-08-04 01:41:43 -07:00
Paul Eggert
f13f2d6815 Parse level options more reliably
* src/tar.c (parse_opt): Don’t mishandle out-of-range LEVEL_OPTION.
2024-08-04 01:41:43 -07:00
Paul Eggert
c26c2ea2e9 Minor utf8.c improvements
* src/utf8.c: Minor rephrases for -1.
2024-08-04 01:41:43 -07:00
Paul Eggert
51c841b927 Simplify ST_DEV_MSB
* src/incremen.c (ST_DEV_MSB):
Use TYPE_WIDTH rather than computing it by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
aca308a778 Use ckd_mul, ckd_add in to_octal, to_base256
* src/create.c (to_octal, to_base256): Simplify.
2024-08-04 01:41:43 -07:00
Paul Eggert
414f635d8b Use ckd_mul, ckd_add in from_header
* src/common.h (LG_64): Remove; no longer used.
* src/list.c (from_header):
Use ckd_mul, ckd_add rather than doing it by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
281e03ec6c Prefer < 0 to == -1 where either will do
Also, fix an unlikely read overflow in sys_exec_setmtime_script.
* src/buffer.c (open_compressed_archive):
* src/compare.c (verify_volume):
* src/exclist.c (info_attach_exclist):
* src/misc.c (xfork):
* src/sparse.c (sparse_scan_file_seek):
* src/system.c (sys_wait_for_child, sys_spawn_shell)
(wait_for_grandchild, sys_wait_command, sys_exec_info_script)
(sys_exec_checkpoint_script, sys_exec_setmtime_script):
* src/transform.c (_single_transform_name_to_obstack):
* src/xattrs.c (xattrs__acls_set, xattrs_acls_get)
(xattrs_xattrs_get, xattrs__fd_set, xattrs_selinux_get)
(xattrs_selinux_set):
* tests/checkseekhole.c (check_seek_hole, main):
Simplify failure tests by just looking at return value sign.
* src/system.c (sys_exec_setmtime_script):
Don’t assume ‘read’ result fits in int.
(sys_exec_setmtime_script): Don’t reject 1 second before Epoch.
2024-08-04 01:41:43 -07:00
Paul Eggert
9cb1293628 xsparse dry runs should not create output
* scripts/xsparse.c (expand_sparse, main): Check for syscall
failure.  Do not create output file if a dry run.
2024-08-04 01:41:43 -07:00
Paul Eggert
44196e198f Better xsparse outname guessing
* scripts/xsparse.c (guess_outname): Use simpler algorithm,
that doesn’t mishandle outnames like ‘/foo’.
2024-08-04 01:41:43 -07:00
Paul Eggert
ba332e36d0 Use xalignalloc
It ports around issues that our handwritten code does not.
* gnulib.modules: Add xalignalloc.
* src/misc.c (ptr_align, page_aligned_alloc): Remove.
All page_aligned_alloc callers changed to use xalignalloc.
2024-08-04 01:41:43 -07:00
Paul Eggert
61656ef35b Make stripped_prefix_len signed
This is part of the general guideline that signed integer types
are safer.
* src/names.c (stripped_prefix_len): Return ptrdiff_t,
not size_t.  All callers changed.
2024-08-04 01:41:43 -07:00
Paul Eggert
fbc60c2334 from_header minor width cleanup
* src/list.c (from_header): Use UINTMAX_WIDTH rather than
computing it by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
a78af4b95e Don’t assume mode_t fits in unsigned long
* src/system.c (oct_to_env): Don’t assume mode_t fits in unsigned
long.  Do not output excess leading 1 bits.  When the mode is
zero, generate "0" rather than "00".  Use sprintf instead of
snprintf, since the output won’t be truncated; in general we don’t
use snprintf unless we want output to be truncated and truncation
is typically not GNU style.
2024-08-04 01:41:43 -07:00
Paul Eggert
c26111742a Prefer C99 formats like %jd to doing it by hand
It’s now safe to assume support for C99 formats like %jd, so remove
some of the longwinded formatting code put in only to be portable to
pre-C99 platforms.
* gnulib.modules: Add intprops.
* src/buffer.c (format_total_stats, try_new_volume)
(write_volume_label):
* src/checkpoint.c (format_checkpoint_string):
* src/compare.c (verify_volume):
* src/create.c (to_chars_subst, dump_regular_file):
* src/incremen.c (read_num):
* src/list.c (read_and, from_header, simple_print_header)
(print_for_mkdir):
* src/sparse.c (sparse_dump_region):
* src/system.c (dec_to_env, sys_exec_info_script)
(sys_exec_checkpoint_script):
* src/xheader.c (out_of_range_header):
Prefer C99 formats like %jd and %ju to STRINGIFY_BIGINT.
* src/common.h: Sort includes.
Include intprops.h, verify.h.  All other includes of verify.h
removed.
(intmax, uintmax): New functions and macros.
(STRINGIFY_BIGINT): Remove; no longer used.
(TIMESPEC_STRSIZE_BOUND): Make it 1 byte bigger, for negatives.
* src/create.c (MAX_VAL_WITH_DIGITS, to_base256):
Use *_WIDTH macros rather than assuming no padding bits.
Prefer UINTMAX_MAX to (uintmax_t) -1.
* src/list.c (tartime): Use strftime result rather
than running strlen later.
* src/misc.c (timetostr): New function.  Prefer it when
printing time_t values.
2024-08-04 01:41:43 -07:00
Paul Eggert
6c91bd82e1 Fix unlikely problems with time overflow
Also, fix some rounding errors while we’re in the neighborhood.
* src/buffer.c (duration_ns, compute_duration_ns): Rename from
‘duration’ and ‘compute_duration’, and count ns rather than s, to
lessen rounding error.  All uses changed.
(compute_duration_ns): Work even if the clock moves backward
and time_t is unsigned.
(print_stats): Don’t worry about null or empty TEXT, as that
cannot happen.  Compare double to UINTMAX_MAX + 1.0, not
to UINTMAX_MAX, so that the comparison is exact.
Handle the unlikely case that numbytes >= UINTMAX_MAX.
* src/tar.c (parse_opt): Treat -L hugenumber as effectively
infinity rather than erroring out.
Prefer ckd_add to checking overflow by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
aae99e863d maint: omit space between "*" and "p" 2024-08-04 01:41:43 -07:00
Paul Eggert
39d315e8ea ptrdiff_t, not int
* src/delete.c (delete_archive_members): Use ptrdiff_t, not int,
to count memory blocks.
(write_recent_bytes): Simplify remainder calculation.
2024-08-04 01:41:43 -07:00
Paul Eggert
bf195d4ae4 ptrdiff_t, not ssize_t
* src/buffer.c (bufmap_reset, _flush_write):
Use ptrdiff_t, not ssize_t, to record pointer differences.
POSIX allows systems where size_t is 64 bits but ssize_t is only 32;
Ultrix used to do that, though no current systems do.
2024-08-04 01:41:43 -07:00
Paul Eggert
a9372cf08a Prefer stdckdint.h to intprops.h
Problem reported by Collin Funk in:
https://lists.gnu.org/r/bug-tar/2024-07/msg00000.html
though this patch is more general than Collin’s suggestion.
* src/compare.c (diff_multivol):
* src/delete.c (move_archive):
* src/sparse.c (oldgnu_add_sparse, pax_decode_header):
* src/system.c (mtioseek):
Prefer ckd_add and ckd_mul to the intprops.h equivalents,
since stdckdint.h is now standard.
2024-08-04 01:41:43 -07:00
Paul Eggert
be1aa32c6d Use ckd_add in page_aligned_alloc
* src/misc.c (page_aligned_alloc): Use ckd_add
instead of doing overflow checking by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
8a3fc52972 Simplify read_header overflow checking
* src/list.c (read_header): Use ckd_add instead of
doing overflow checking by hand.  Although the old code
was correct on all practical hosts, the new code is simpler
and works even on weird hosts where SIZE_MAX <= INT_MAX.
2024-08-04 01:41:43 -07:00
Paul Eggert
927d67855e Cleaner overflow checking in xheader_read
* src/xheader.c (xheader_read): Prefer ckd_add to
doing overflow checking by hand.
2024-08-04 01:41:43 -07:00
Paul Eggert
c6a5af16ba maint: use static_assert
* gnulib.modules: Add assert-h, for static_assert.
* src/common.h, src/list.c, src/misc.c:
Prefer static_assert to #if + #error.  This doesn’t fix any bugs; it’s
just that in general it’s better to avoid the preprocessor.
2024-08-04 01:41:43 -07:00
Paul Eggert
dcc90722ac Fix tests/ckmtime.c arithmetic
* tests/ckmtime.c (main): Don’t assume time_t is signed.
Avoid integer overflows (quite possible if time_t is 32 bit).
Do calculations precisely, without any rounding errors.
2024-08-04 01:41:43 -07:00
Paul Eggert
7557fdd4df Fix unlikely overflow in utf8_convert
* src/utf8.c (utf8_convert): Check for integer overflow.
2024-08-04 01:41:43 -07:00
Paul Eggert
91ee466c8a Fix unlikely overflow in transform.c
* src/transform.c (_single_transform_name_to_obstack):
Use xinmalloc to check for integer overflow.
2024-08-04 01:41:43 -07:00
Paul Eggert
7079fc369b Better overflow checking for blocking factor
* src/tar.c (parse_opt): Use ckd_add and ckd_mul instead of
less-obvious code that relies on implementation-defined
conversions.
2024-08-04 01:41:43 -07:00
Paul Eggert
b26e798a0f xsparse cleanup, including integer overflow
* scripts/xsparse.c: Include inttypes.h, for strtoimax.
Don’t include stdint.h, since inttypes.h includes it.
Sort include directives.
Make all extern functions and vars static, except for ‘main’.
(string_to_off): Use strtoimax instead of doing overflow
checking by hand, incorrectly (it relied on undefined behavior).
(string_to_size): New arg MAXSIZE.  All callers changed.
(get_var): Return bool not int.  Fix unlikely integer overflow.
Use strncmp instead of memcmp, to avoid unlikely pointer overflow.
(read_xheader, read_map, main): Avoid unlikely integer overflow.
Check for I/O errors more consistently.
(main): Prefer bool to int, and put vars near use.
2024-08-04 01:41:43 -07:00
Paul Eggert
f22b9fe3ce maint: fix some unlikely wordsplit overflows
* gnulib.modules: Add reallocarray.
* lib/wordsplit.c: Include stdckdint.h.
(ISDELIM, expvar, isglob, scan_word):
Defend against strchr (s, 0) always succeeding.
(alloc_space, wsplit_assign_vars):
Fix some unlikely integer overflows, partly by using reallocarray.
(alloc_space): Avoid quadratic worst-case behavior.
(isglob): Return bool, not int.  Accept size_t, not int.
(to_num): Remove; no longer used.
(xtonum): Clarify the code the bit.  Rely on portable
conversion to unsigned char rather than problematic pointer cast.
2024-08-04 01:41:42 -07:00
Paul Eggert
8f094605a8 maint: prefer C23 if available
* gnulib.modules: Add std-gnu23.
2024-07-29 00:41:34 -07:00
Paul Eggert
26d1e4ddbc Add some gnulib.modules
* gnulib.modules: Add errno, limits-h, safe-read, sys_stat.
Not sure about the relationship between gnulib.modules
and paxutils/gnulib.modules, but anyway tar itself uses
these so we should depend on them.  (Perhaps it would be
better if there was just one Gnulib module list for tar;
that would be less confusing.)
2024-07-29 00:41:34 -07:00
Paul Eggert
ec35690e91 build: update gnulib and paxutils submodules to latest 2024-07-29 00:41:34 -07:00
Paul Eggert
3fa1fd0751 Pacify gcc 14 -Wanalyzer-null-argument
* src/tar.c (optloc_eq): Add another ‘assume’.
2024-07-27 00:26:42 -07:00
Paul Eggert
fd33f25989 Pacify gcc 14 -Wanalyzer-infinite-loop
* gnulib.modules: Add stddef, for ‘unreachable’.
* src/compare.c (dumpdir_cmp): Tell GCC that the default case
is unreachable.  Make just one pass through the string,
instead of two passes (one via strcmp, another via strlen).
2024-07-27 00:26:42 -07:00
Paul Eggert
45a86d45b2 maint: make a few funcs and vars static
* src/buffer.c (last_stat_time, write_fatal_details):
* src/tar.c (name_more_files):
* src/xattrs.c (xheader_xattr_add):
Now static.
2024-07-26 23:44:03 -07:00
Paul Eggert
afd073399a maint: remove GLOBAL as per GCC 14
* src/common.h (GLOBAL): Remove this macro, and all its uses.
It collides with GCC 14 and -Wmissing-variable-declarations.
Change all uses of GLOBAL to use extern instead,
and declare the variables in their respective .c files.
Move .c file’s extern declarations here, so that they
appear only once and are checked against definitions.
* src/names.c (unconsumed_option_tail): Now static.
2024-07-26 23:44:03 -07:00
Paul Eggert
9f1c32c18b Modernize use of Gnulib, paxutils
* configure.ac: Omit stuff no longer needed now that Gnulib or
paxlib does it, or the code no longer needs the configure-time checks.
Do not use AC_SYS_LARGEFILE (Gnulib largefile does this) or check
for fcntl.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/time.h, sys/tprintf.h, sys/tape.h, unistd.h,
locale.h, netdb.h; these are all now standard, or old ways of getting
at magtapes are no longer needed and we now have only sys/mtio.h.
Do not check for lstat, readlink, symlink, and check only for
waitpid’s existence rather than attempting to replace it.
Do not check for decls of getgrgid, getpwuid, or time.
Check just once for iconv.h.
* gnulib.modules: Add largefile.
* lib/.gitignore, lib/Makefile.am (noinst_HEADERS, libtar_a_SOURCES):
Remove system-ioctl.h, which is no longer in paxlib.
All includes now changed to just check HAVE_SYS_MTIO_H directly.
* lib/wordsplit.c (wordsplit_c_escape_tab, wordsplit_errstr)
(wordsplit_nerrs):
Now static or an enum, and without any leading "_" in the name.
* src/buffer.c (record_start, record_end, current_block, records_read):
* src/delete.c (records_skipped): Add extern decl to pacify GCC.
* src/compare.c, src/create.c, src/extract.c: Omit uses of
HAVE_READLINK and HAVE_SYMLINK since we now let Gnulib deal with
platforms lacking readlinkat and symlinkat.
* src/system.c: Use "#if !HAVE_WAITPID" instead of "#if MSDOS".
2024-07-26 21:56:20 -07:00
Paul Eggert
4e0deb7416 build: update gnulib and paxutils submodules to latest 2024-07-26 21:56:20 -07:00
Paul Eggert
3d2c735b7c maint: higher-precision checkpoint timestamps
* src/checkpoint.c (format_checkpoint_string):
Use current_timespec to get nanosecond resolution.
This also frees us from the necessity of including <sys/time.h>
to use gettimeofday, which is removed in POSIX.1-2024.
2024-07-24 10:04:16 -07:00
Paul Eggert
bd066ac0a5 build: update gnulib and paxutils submodules to latest 2024-07-24 10:04:16 -07:00
Paul Eggert
b96cabb1ea Sync bootstrap from Gnulib 2024-07-24 10:04:16 -07:00
Paul Eggert
42d1143dd5 Sync bootstrap from Gnulib 2024-07-15 14:59:33 -07:00
Paul Eggert
2b9e2cc947 maint: adjust to Gnulib -Wsystem-headers change
* configure.ac: Do not suppress -Wsystem-headers, as
Gnulib no longer enables it.
2024-07-15 14:59:33 -07:00
Paul Eggert
1752231f9e build: update gnulib submodule to latest 2024-07-15 14:59:33 -07:00
Sergey Poznyakoff
5f2cda027d Various formatting fixes 2024-06-07 00:05:00 +03:00
Sergey Poznyakoff
1e6ce98e3a Fix spurious diagnostic during extraction of . with --keep-newer-files
Bug reported in https://savannah.gnu.org/bugs/?65838.

Bug introduced by 79d1ac38c1.

* src/extract.c (make_directories): Restore second argument.  This
reverts the change made in 79d1ac38c1.
(maybe_recoverable, rename_directory): Update calls to make_directories.
* tests/extrac27.at: New file.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Likewise.
2024-06-05 18:19:10 +03:00
Paul Eggert
883f2e6dca tar: fix current_block confusion
Problem reported by Robert Morris in:
https://lists.gnu.org/r/bug-tar/2024-03/msg00001.html
* src/delete.c (flush_file): Simply return at EOF,
so that current_block continues to point to end of input.
2024-03-03 13:28:23 -08:00
Paul Eggert
628c49250a tar: fix unlikely overflow
* src/delete.c (flush_file): Fix arithmetic overflow if
TYPE_MAXIMUM (off_t) - BLOCKSIZE < current_stat_info.stat.st_size.
2024-03-03 13:28:23 -08:00
Paul Eggert
21318f3856 tar: improve diagnostic for truncated archive
* src/buffer.c (seek_archive): If EOF has been read, don’t attempt
to seek past it.  This replaces a bogus "rmtlseek not stopped at a
record boundary" message with a better "Unexpected EOF in archive"
when I run ‘tar tvf gtar13c.tar’ using the gtar13.tar file here:
https://lists.gnu.org/r/bug-tar/2024-03/msg00001.html
2024-03-03 13:28:23 -08:00
Sergey Poznyakoff
fac2b4c11a Avoid gcc 13 "unused parameter" warnings 2024-01-16 14:28:19 +02:00
Sergey Poznyakoff
6ba24c31c6 Recognize suffixes .z (gzip) and .tzo (lzop) 2024-01-15 22:52:27 +02:00
Sergey Poznyakoff
7b65ae35ab Fix description of the CACHEDIR.TAG file. 2024-01-15 22:30:22 +02:00
Sergey Poznyakoff
f622c07108 Fix --exclude-ignore option.
This fixes https://savannah.gnu.org/bugs/?64387.

* src/exclist.c (info_attach_exclist): Always use file->flags.
(exclude_vcs_ignores): Pass flags from struct vcs_ignore_file
to excfile_add.

* tests/exclude19.at: New test.
* tests/exclude20.at: New test.
* tests/Makefile.am: Add new tests.
* tests/testsuite.at: Add new tests.
2024-01-15 21:24:34 +02:00
Sergey Poznyakoff
d763055edd Fix missing space in the manual 2024-01-15 20:21:26 +02:00
Sergey Poznyakoff
b4d1fa77b6 When given -c -a, issue a warning if no compressor is associated with the suffix.
* src/suffix.c (find_compression_suffix): Always return stripped
archive name length in the last argument.  Return 0 if there is no
suffix.
(find_compression_program): Remove.
(set_compression_program_by_suffix): Take third argument, controlling
whether to issue a warning if no suitable compression program is found
for the suffix.
* src/common.h (set_compression_program_by_suffix): Change prototype.
* src/buffer.c, src/tar.c: All uses of set_compression_program_by_suffix
changed.
2024-01-15 00:00:02 +02:00
Paul Eggert
66eaa2f821 Port EOF detection test to macOS
* tests/delete06.at: Don’t assume the diagnostic is
“Value too large for defined data type”, as strerror
uses a different wording on macOS 12.6.
2024-01-01 19:12:10 -08:00
Paul Eggert
24a2fcfd83 Skip test on macOS 12.6
* tests/xform04.at: Skip test on macOS 12.6, which is behind the times
and doesn’t think that ⱥ (U+2C65 LATIN SMALL LETTER A WITH STROKE) is
printable.
2024-01-01 19:12:10 -08:00
Paul Eggert
835b0c7dee Port --numeric-owner basic tests to macOS
* tests/numeric.at: If chown fails, skip the test.
This is needed on macOS 12.6 if the user has group
ID 4294967295 (nogroup), which chown rejects.
2024-01-01 19:12:10 -08:00
Paul Eggert
c6f0ad5117 Update copyright years
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
gnulib/build-aux/update-copyright \
  $(git ls-files | sed -e '/^gnulib$/d
			   /^paxutils$/d
			   /^COPYING$/d
			   /\/fdl.texi$/d')
sed -i '2000,${
    /^Copyright @copyright/d
    s/^[0-9]*--\(2024 Free Software Foundation, Inc.\)/Copyright (C) \1/
  }' doc/tar.texi
2024-01-01 19:08:46 -08:00
Paul Eggert
f0098df0b3 doc: fix date in example
* doc/tar.texi: Fix copyright date in example.
2024-01-01 19:08:46 -08:00
Paul Eggert
3a27df1d69 build: update gnulib submodule to latest 2024-01-01 19:08:46 -08:00
Paul Eggert
c1e277476c Support multi-byte --transform='...\L...' etc
Support upcasing and downcasing in multi-byte locales.
* gnulib.modules: Add c32rtomb, c32tolower, c32toupper,
mbrtoc32-regular.
* src/transform.c: Do not include ctype.h.  Include mcel.h.
(stk, stk_init): Move up.
(run_case_conv): Return void, not char *.  Append result to
stk directly; this avoids the need for a separate allocation.
All callers changed.  Do not assume a single-byte locale.
* tests/xform04.at: New test.
* tests/Makefile.am (TESTSUITE_AT):
* tests/testsuite.at: Add it.
2023-09-12 23:23:41 -05:00
Paul Eggert
783321ff1b Simplify wordsplit_string_unquote_copy
* lib/wordsplit.c (wordsplit_string_unquote_copy): Simplify.
2023-09-12 23:23:41 -05:00
Paul Eggert
01f986b921 Parse in a more locale-independent way
update submodules to latest
* gnulib.modules: Add c-ctype.
* lib/wordsplit.c, src/buffer.c, src/exclist.c, src/incremen.c:
* src/list.c, src/misc.c, src/names.c, src/sparse.c, src/tar.c:
* src/xheader.c:
Include c-ctype.h, and use its API rather than ctype.h’s.
This is more likely to work when oddball locales are used.
* src/transform.c: Include ctype.h, since this module still uses
tolower and toupper (this is probably wrong - should be multi-byte).
2023-09-12 23:23:41 -05:00
Paul Eggert
05fcfaafb6 Use single is_octal_digit function
* src/list.c (ISOCTAL): Remove.
(is_octal_digit): New static function.
All uses of ISOCTAL and ISODIGIT replaced with is_octal_digit.
2023-09-12 23:23:40 -05:00
Paul Eggert
e35fe3a77c maint: fix THANKS misspellings
* THANKS: Fix recently-introduced misspellings.
2023-09-11 09:32:18 -05:00
Paul Eggert
78d4ccd755 Fix pointer bug in drop_volume_label_suffix
Problem reported by Marc Espie in:
https://lists.gnu.org/r/bug-tar/2023-09/msg00003.html
* src/buffer.c (drop_volume_label_suffix):
Redo to not compute a pointer before the start of a buffer,
as this is not portable.
2023-09-11 01:17:29 -05:00
Paul Eggert
9599d193b8 quote unknown header keywords in diagnostics
* src/xheader.c (decx): Quote unknown header in warning, as it may
contain control characters.  Problem reported by Wicher Minnaard.
2023-09-11 01:17:29 -05:00
Paul Eggert
e1bba5e7dd Prefer mcel to mbuiter
Prefer the lighter-weight mcel implementation to the heavier-weight
mbuiter that GNU tar does not need.
* bootstrap.conf (avoided_gnulib_modules): Avoid mbuiter, mbuiterf.
* gnulib.modules: Add mcel-prefer.
2023-09-10 10:12:20 -07:00
Paul Eggert
f2613580c7 build: update gnulib submodule to latest 2023-09-10 10:12:20 -07:00
Markus Mayer
1cdad4cc28 .gitmodules: switch to HTTPS
HTTPS is not only encrypted but also better suited for corporate
firewalls. Let's use it to clone submodules.
Copyright-paperwork-exempt: yes
2023-09-07 14:33:37 -07:00
Sergey Poznyakoff
ecdef6677b docs: replace references to fileutils with coreutils.
GNU fileutils is decommissioned and superseded by coreutils.
This fixes Savannah bug #64495.
2023-08-22 23:55:09 +03:00
Sergey Poznyakoff
5114218025 Fix Savane bug #64581
This reverts commit 4f3824743f50808a0079e6057107de53c4a25f22.
2023-08-22 18:34:20 +03:00
Paul Eggert
a9a8990fb3 Bump extrac26 timeout
* tests/extrac26.at: Increase timeout from 15 to 60 s.
On my old machine it took 15 s.
2023-08-21 13:42:14 -07:00
Paul Eggert
12b58a69aa Simplify recently-added hash code
* src/extract.c (delay_set_stat): Simplify hash lookup;
no need to initialize members other than file_name.
Avoid assignment in ‘if’ when it’s easy.
(extract_finish): Do not bother to free when we are about to exit.
2023-08-21 13:42:14 -07:00
Benjamin Woodruff
a5afb36765 Fix O(n^2) time bug in --delay-directory-restore
delayed_set_stat avoids inserting duplicate entries into
delayed_set_stat_head. It was doing this by scanning the entire
list.

Normally this list is small, but if --delay-directory-restore is
used (including automatically for incremental archives), this list
grows with the total number of directories in the archive.

The entire scan takes O(n) time. Extracting an archive with n
directories could therefore take O(n^2) time.

The included test uses AT_SKIP_LARGE_FILES, allowing it to optionally be
skipped. It may execute slowly on certain filesystems or disks, as it
creates thousands of directories.

There are still potentially problematic O(n) scans in
find_direct_ancestor and remove_delayed_set_stat, which this patch does
not attempt to fix.

* NEWS: Update.
* src/extract.c (delayed_set_stat_table): Create a table for O(1)
lookups of entries in the delayed_set_stat_head list. The list
remains, as tracking insertion order is important.
(dl_hash, dl_compare): New hash table helper functions.
(delay_set_stat): Create the hash table, replace the O(n) list scan
with a hash_lookup, insert new entries into the hash table.
(remove_delayed_set_stat): Also remove entry from hash table.
(apply_nonancestor_delayed_set_stat): Also remove entry from hash
table.
(extract_finish): Free the (empty) hash table.
* tests/extrac26.at: New file.
* tests/Makefile.am (TESTSUITE_AT): Include extrac26.at.
* tests/testsuite.at: Include extrac26.at.
2023-08-21 13:42:14 -07:00
Paul Eggert
bfee1d44a3 Pacify gcc -Wanalyzer-fd-use-without-check
* src/system.c (sys_exec_setmtime_script):
Treat fds with more care.
2023-08-21 13:42:14 -07:00
Paul Eggert
8131ca7b26 build: update gnulib submodule to latest 2023-08-21 13:42:14 -07:00
Sergey Poznyakoff
d437ecf75d Revert "Fix savannah bug #63567"
Commit e89c7a45eb broke deletion from archives. The reported number
of bytes read is rounded to the nearest record anyway, revert the
commit and document the fact.

Reported by Ed Santiago. See
https://bugzilla.redhat.com/show_bug.cgi?id=2230127

* doc/tar.texi: Document the fact that --totals rounds up the
number of bytes reads to the nearest record.
* src/buffer.c: Revert changes.
* tests/delete06.at: Fix expected status code and stderr.
2023-08-15 11:34:21 +03:00
Paul Eggert
8e5483577d Stop using alloca
* gnulib.modules: Remove alloca.
* src/create.c (dump_file0): Return address of any allocated
storage.  Caller changed to free it.  Use xmalloc instead
of alloca, to obtain this storage.
* src/list.c (from_header): Use quote_mem instead of quote,
removing the need to use alloca.
2023-08-02 09:02:33 -07:00
Sergey Poznyakoff
9a30bb2674 New option: --set-mtime-command
* NEWS: Document new option.
* src/common.h (COMMAND_MTIME): New constant.
* src/create.c (set_mtime_command)
(set_mtime_format): New globals.
(sys_exec_setmtime_script): New prototype.
* src/system.c (start_header): Handle COMMAND_MTIME.
* src/tar.c (sys_exec_setmtime_script): New function.
2023-08-01 15:45:14 +03:00
Paul Eggert
eb30aa7801 * doc/tar.texi: Fix minor reproducibility typos. 2023-07-25 09:45:46 -07:00
Paul Eggert
68636f0bcb Improve reproducibility recipe
* doc/tar.texi (Reproducibility): Improve index.
Improve and add comments to recipe.  In the recipe,
don’t worry about file names beginning with ‘-’ for simplicity;
don’t use touch -c as it exits with status 0 even when it
does not work; and set directory timestamps too.
2023-07-25 09:43:57 -07:00
Sergey Poznyakoff
8ed95e92ef * doc/tar.texi: Define reproducibility. 2023-07-25 09:43:57 -07:00
Paul Eggert
d1ca333391 New doc about reproducible archives
* doc/tar.texi (Reproducibility): New section.
Spruce some other sections related to timestamps etc.
2023-07-24 14:46:53 -07:00
Paul Eggert
71530f72d2 tests: fix LDADD
Problem reported by Christian Weisgerber <naddy@mips.inka.de> in:
https://lists.gnu.org/r/bug-tar/2023-07/msg00015.html
* tests/Makefile.am (LDADD): Add $(LIBINTL), $(LIBICONV).
2023-07-19 15:49:18 -07:00
Sam James
18f90676e4 tests: Fix xz tests by unsetting XZ_OPT, XZ_DEFAULTS
Copyright-paperwork-exempt: true
2023-07-18 09:57:24 -07:00
Sam James
7687bf4acc tests: Fix bashism in testsuite.at
&> is a bashism and causes various tests to fail with /bin/sh as non-bash
(e.g. dash). Use the same pattern the rest of the file uses instead of &>.

Copyright-paperwork-exempt: true
2023-07-18 09:47:26 -07:00
Paul Eggert
39849e9d91 tests: fix TESTSUITE_AT
Problem reported by Lukas Javorsky <ljavorsk@redhat.com> in:
https://lists.gnu.org/r/bug-tar/2023-07/msg00002.html
* tests/Makefile.am (TESTSUITE_AT): Add exclude17.at, exclude18.at.
Remove compress.m4; all uses changed.  Add a comment saying how
to rederive this.  Sort.
2023-07-18 09:31:17 -07:00
Sergey Poznyakoff
8632df398b Fix savannah bug #64441
* src/Makefile.am (tar_LDADD): Add libiconv libraries.
2023-07-18 17:02:23 +03:00
339 changed files with 7341 additions and 6692 deletions

4
.gitmodules vendored
View File

@ -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

View File

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

View File

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

View File

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

View File

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

74
NEWS
View File

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

2
README
View File

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

View File

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

View File

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

7
THANKS
View File

@ -283,7 +283,7 @@ Joutsiniemi Tommi Il tj75064@cs.tut.fi
Joy Kendall jak8@world.std.com
Judy Ricker jricker@gdstech.grumman.com
Juha Sarlin juha@tds.kth.se
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
View File

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

View File

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

453
bootstrap
View File

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

View File

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

View File

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

View File

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

2
doc/.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

2
gnulib

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

View File

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

1
lib/.gitignore vendored
View File

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

View File

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

View File

@ -1,5 +1,5 @@
/* Replacement <attr/xattr.h> for platforms that lack it.
Copyright 2012-2023 Free Software Foundation, Inc.
Copyright 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,7 +1,7 @@
/* openat-style fd-relative functions for operating with extended file
attributes.
Copyright 2012-2023 Free Software Foundation, Inc.
Copyright 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* Prototypes for openat-style fd-relative functions for operating with
extended file attributes.
Copyright 2012-2023 Free Software Foundation, Inc.
Copyright 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -51,7 +51,7 @@ int lsetxattrat (int dir_fd, const char *path, const char *name,
const void *value, size_t size, int flags);
/* dir-fd-relative getxattr. Operation gets the VALUE of the extended
attribute idenfified by NAME and associated with the given PATH in the
attribute identified by NAME and associated with the given PATH in the
filesystem relatively to directory identified by DIR_FD. For more info
about all parameters see the getxattr(2) manpage. */
ssize_t getxattrat (int dir_fd, const char *path, const char *name,

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

119
src/map.c
View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

974
src/tar.c

File diff suppressed because it is too large Load Diff

133
src/tar.h
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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