415 Commits

Author SHA1 Message Date
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 4f3824743f.
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
Sergey Poznyakoff
e545d446df Version 1.35 2023-07-18 09:38:30 +03:00
Sergey Poznyakoff
e7a5e12445 Use full-read instead of safe-read
This helps handle archiving on certain filesystems where read()
returns less bytes than requested when reading from a regular
file.

References:

  https://savannah.gnu.org/bugs/index.php?64426
  https://lists.gnu.org/archive/html/bug-tar/2021-07/msg00001.html

* gnulib.modules: Add full-read.
* src/common.h: Include full-read.h
* src/misc.c: Use full_read.
* src/sparse.c: Likewise.
* src/update.c: Likewise.
2023-07-17 17:07:12 +03:00
Sergey Poznyakoff
31f68bbe2a Fix typo in comments
This and the bug fixed by the previous commit were reported by
Benno Schulenberg.
2023-07-11 09:16:15 +03:00
Sergey Poznyakoff
10954cf163 Fix --update --wildcards
* src/common.h (name): New field: is_wildcard.
(name_scan): Change protoype.
* src/delete.c: Update calls to name_scan.
* src/names.c (addname, add_starting_file): Initialize is_wildcard.
(namelist_match): Take two arguments.  If second one is true, return
only exact matches.
(name_scan): Likewise.  All callers updated.
(name_from_list): Skip patterns.
* src/update.c (remove_exact_name): New function.
(update_archive): Do not remove matching name, if it is a pattern.
Instead, add a new entry with the matching file name.

* tests/update04.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Include new test.

* NEWS: Update.
2023-07-10 21:25:30 +03:00
Benno Schulenberg
b3a71dbdb9 Various fixes in the documentation
* doc/tar.1: Add missing dots, use plural when necessary,
tweak a wording.  Remove an incorrect observation, three times.
Add some missing articles, correct some formatting,
and expand the opaque descriptions of two options.
* doc/tar.texi: Drop a stray `cd` command from an example.
Correct two cross references, correct the paragraph
about the manpage, and unbreak a URL.
* src/names.c: Correct and shorten an error message: "non-optional"
means "mandatory", but "non-option" is what was meant.  And the
phrase "in archive create or update mode" was both unneeded and
incomplete.
* tests/positional01.at: Change expected error text.
* tests/positional02.at: Likewise.
* tests/positional03.at: Likewise.
2023-07-10 12:33:34 +03:00
Paul Eggert
cf16a23945 build: update gnulib submodule to latest 2023-06-28 17:43:48 -07:00
Paul Eggert
d6a60bba76 tar: extract delayed links in order
Extract delayed links in tar file order, rather than
in hash table order with modifications.
This is simpler and more likely to use the kernel’s
cached filesystem data, assuming related delayed links
are nearby in the tar file.
* src/extract.c (struct delayed_link.has_predecessor):
Remove.  All uses removed.
(delayed_link_head, delayed_link_tail): New static vars.
This resurrects delayed_link_head’s old function
except that the linked list is now in forward order, not reverse.
(find_delayed_link_source): Now simply returns bool,
since the callers no longer need the pointer.
(create_placeholder_file):
Put the delayed link at the end of the linked list.
Omit no-longer-needed last arg.  All callers changed.
(apply_delayed_links): Simplify now that we can just iterate
through the delayed_link_head list.
2023-06-25 14:28:36 -07:00
Paul Eggert
2ccd643d01 tar: make safe for -Wunused-parameter
This also ports to C23 [[maybe_unused]].
* configure.ac (WARN_CFLAGS): Do not add -Wno-unused-parameter.
Add MAYBE_UNUSED where needed in source code.
Also, put it at the front where C23 requires it.
2023-06-25 14:28:36 -07:00
Paul Eggert
fdff045d4b * po/.gitignore: Omit redundant *~ entries. 2023-06-25 14:28:36 -07:00
Paul Eggert
90cceec4bb tar: omit unnecessary freeing
* src/tar.c (main): Omit unnecessary freeing.
2023-06-25 14:28:36 -07:00
Sergey Poznyakoff
826c5eb64e Make sure each delayed link entry is visited once
* src/extract.c (create_placeholder_file): Use FLEXNSIZEOF (overlooked
by c542d3d0c8)
(apply_delayed_links): Don't follow the "next" chain after its entries
have been applied.
2023-06-18 09:20:28 +03:00
Paul Eggert
2096772fbe Omit unnecessary freeing
* src/extract.c (apply_delayed_link): Don’t bother to
free memory, as we’re about to exit.
2023-06-17 00:37:43 -07:00
Paul Eggert
c542d3d0c8 Port to strict C99 struct hack
Portability bug caught by GCC 13 -fstrict-flex-arrays.
* gnulib.modules: Add flexmember.
* src/create.c (struct link):
* src/exclist.c (struct excfile):
* src/extract.c (struct delayed_link, struct string_list):
Include <flexmember.h>.  Use FLEXIBLE_ARRAY_MEMBER, for
portability to strict C99 or later.  All storage
allocations changed to use FLEXNSIZEOF.
2023-06-16 17:12:40 -07:00
Paul Eggert
4c7a3798d8 Use Gnulib ‘dup2’ module
This simplifies code that would otherwise use dup and close.
* gnulib.modules: Add dup2.
* src/system.c: Add #pragma to pacify GCC 13.
(xdup2): Simplify by using dup2.
2023-06-16 17:12:40 -07:00
Paul Eggert
5d6736e394 Use Gnulib ‘free-posix’ module
Paxutils uses it so we should too.
* gnulib.modules: Add ‘free-posix’.
* src/misc.c (remove_any_file): Assume ‘free’ keeps errno.
2023-06-16 17:12:39 -07:00
Paul Eggert
9ee30c9804 * buffer.c: work around GCC bug 109856 2023-06-16 17:12:39 -07:00
Paul Eggert
4695adfd59 Recommend Y2038 safety 2023-06-16 17:12:39 -07:00
Paul Eggert
a484178a18 build: update gnulib and paxutils submodules to latest 2023-06-16 17:12:39 -07:00
Paul Eggert
e39b93d822 * .gitignore: Add slash, as bootstrap does this now. 2023-06-16 17:12:39 -07:00
Paul Eggert
8ccfc4e416 Sync bootstrap from Gnulib 2023-06-16 17:12:39 -07:00
Pavel Raiskup
e7987b72c6 Comment a bit on the xattr extraction logic
* src/extract.c (extract_file): Document why we pre-create with S_IWUSR.
(set_xattr): Drop the INVERT_PERMISSIONS doc leftover.
2023-06-06 12:37:06 +03:00
Pavel Raiskup
06805b9281 Fix --xattr-include='*' documentation
* doc/tar.texi (Extended File Attributes): The default extraction
pattern consists of just 'user.*' namespace only.  While on it, try
to explain the reasons for this default behavior.
2023-06-06 12:34:08 +03:00
Sergey Poznyakoff
5461025569 Honor --numeric-owner when storing ACL entries
Based on patch from Fabian Grünbichler <f.gruenbichler@proxmox.com>

* src/xattrs.c (acls_get_text): New function.  If given --numeric-owner,
use acl_to_any_text to convert ACL to textual representation.  Print
warning if that function is not available.
(xattrs__acls_get_a, xattrs__acls_get_d): Use acls_get_text.
2023-05-18 13:35:50 +03:00
Sergey Poznyakoff
3f2b847ddd Upgrade paxutils to d50ea31268
This fixes two bugs related to remote archives:

1. Access from unprivileged user accounts.
2. Malfunctioning seek (L) command.
2023-05-18 11:44:50 +03:00
Sergey Poznyakoff
6af0596726 Don't copy DISTFILES during bootstrapping
DISTFILES are necessary only during bootstrapping and should not be
distributed.

This partially reverts commit 9f0e54ab2.
2023-05-18 08:43:04 +03:00
Benno Schulenberg
204b414d11 Add missing option to manpage and remove duplicate operation
* doc/tar.1: Add needed option -f after operation -A, sort operation -t
  alphabetically, add --file after --concatenate, consistently use long
  option --file in the GNU-style section, and delete duplicate --update.
* doc/tar.texi: Add small missing word, and lowercase a letter.
2023-05-01 20:31:47 +03:00
Sergey Poznyakoff
6a1581240b Change some wording in doc/intern.texi 2023-04-18 11:15:44 +03:00
Anton Makrushin
4f814e0e4c Exclude VCS directory with writing from an archive
See https://savannah.gnu.org/bugs/?62859
2023-04-18 10:50:57 +03:00
Sergey Poznyakoff
4177c98bcc Detect EOF when deleting from archive.
See https://savannah.gnu.org/bugs/?63823

* src/delete.c (flush_file): Break the loop on EOF.
* tests/delete06.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Likewise.
2023-02-21 14:00:32 +02:00
Sergey Poznyakoff
5e8a915b16 Changes in extended header decoder
* src/xheader.c (decode_time): Fix error detection.
(raw_path_decoder): Ignore empty paths.
2023-02-11 14:21:05 +02:00
Sergey Poznyakoff
edf38d13a4 Prevent dead loop in extract_file
* src/extract.c (maybe_recoverable): If make_directories indicates
success, suppose some intermediate directories have been made, even
if in fact they have not.  That's necessary to avoid dead loops when
maybe_recoverable is called with the same arguments again.
2023-02-11 13:03:23 +02:00
Sergey Poznyakoff
3da78400ea Fix boundary checking in base-256 decoder
* src/list.c (from_header): Base-256 encoding is at least 2 bytes
long.
2023-02-11 11:59:52 +02:00
Paul Eggert
02402920f8 Adjust to Gnulib macro renaming
* src/Makefile.am, tests/Makefile.am (LDADD):
Gnulib renamed LIB_HAS_ACL to FILE_HAS_ACL_LIB.
2023-01-06 12:50:36 -08:00
Paul Eggert
021845e54d build: update gnulib, paxutils submodules to latest 2023-01-06 12:50:36 -08:00
Paul Eggert
0f289d7238 Go back to single-file bootstrap
Gnulib now supports a single-file bootstrap with --pull
and --gen options, in place of the three files
autopull.sh, autogen.sh, bootstrap-funclib.sh.
This keeps the top level a bit cleaner.
* bootstrap: Sync from Gnulib build-aux/bootstrap
instead of from top/bootstrap.
* autopull.sh, autogen.sh, bootstrap-funclib.sh: Remove.
2023-01-06 12:50:36 -08:00
Paul Eggert
71d1619abd Fix ‘assume’ include
* src/tar.c: Include verify.h, needed for ‘assume’.
2023-01-06 12:50:36 -08:00
Paul Eggert
719d3b44b7 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')
2023-01-06 12:50:36 -08:00
Sergey Poznyakoff
a65f01ac35 Optionally warn about missing zero blocks at the end of the archive
(In response to savannah bug #63574)

* doc/intern.texi: Document actual tar behaviour in regard to
missing end-of-file marker.
* doc/tar.texi: Rewrite the "warnings" section.  Document
--warning=missing-zero-blocks
* src/common.h (WARN_MISSING_ZERO_BLOCKS): New constant.
(WARN_ALL): Include all warning bits.
* src/list.c (read_and): If EOF is reached without seeing end-of-file
blocks and the "missing-zero-blocks" warning is requested, warn about
the fact.
* src/warning.c: New warnings: "missing-zero-blocks", "verbose".
(warning_option): Change definition to reflect changes in common.h
2022-12-29 17:42:04 +02:00
Sergey Poznyakoff
2cde05fa10 Fix example use of find command in documentation 2022-12-27 13:02:05 +02:00
Sergey Poznyakoff
e89c7a45eb Fix savannah bug #63567
* src/buffer.c (short_read): Increase records_read only if a full
record has been read.
2022-12-26 23:46:15 +02:00
Paul Eggert
24c8306965 Fix -Af F bug when F is not a regular file
Problem reported by Boris Gjenero in:
https://lists.gnu.org/r/bug-tar/2022-11/msg00001.html
* src/update.c (append_file): Don’t assume that FILE_NAME is a
regular file whose size can be determined before reading.
Instead, simply read from the file until its end is reached.
2022-11-03 23:07:38 -07:00
Paul Eggert
ba26ec86e2 Fix README-* files
README-alpha is for alpha releases, which are not from Git or CVS, so
omit mention of that.  I'm not sure we'll ever do alpha releases, but
if we do, README-alpha assumes the tarballs are already bit.

Update README-hacking with info that was mistakenly put into
README-alpha.  Also mention Bison, needed for parse-date.y.
2022-11-03 22:58:27 -07:00
Paul Eggert
de64229632 Fix undefined behavior on bad extended header
* src/xheader.c (locate_handler): Avoid undefined behavior
if strlen(keyword) < kwlen.
2022-10-27 13:20:38 -07:00
Sergey Poznyakoff
17debecd73 Fix savannah bug #63123
The bug was introduced by commit 79d1ac38c1, which didn't take into
account all the consequences of returning RECOVER_OK on EEXIST, in
particular interactions with the delayed_set_stat logic.

The commit 79d1ac38c1 is reverted (the bug it was intended to fix
was actually fixed by 79a442d7b0).  Instead:

* src/extract.c (maybe_recoverable): Don't call maybe_recoverable
if EEXIST is reported when UNLINK_FIRST_OLD_FILES option is set.
2022-10-22 19:59:04 +03:00
Sergey Poznyakoff
02f9af1b8d Fix savannah bug #63250
* src/buffer.c (magic): Add signature of LZMA files produced by xz.
2022-10-22 11:14:53 +03:00
Paul Eggert
87f9e42108 Mention previous bug fix 2022-10-07 15:55:44 -07:00
Paul Eggert
0f0722df45 tests: check for recently-fixed bug
* tests/exclude17.at: New file.
* tests/testsuite.at (AT_CHECK_TIMESTAMP): Add it.
2022-10-07 15:25:36 -07:00
Aurélien Martin
7324326b1d tar: fix --exclude-vcs-ignores memory
The function frees the patterns' wordsplit structure without asking
add_exclude to reallocate the strings.  In many cases, this leads to
each file name in the directory being checked against the memory
location where it just got reallocated.
* src/exclist.c: Use EXCLUDE_ALLOC.
Copyright-paperwork-exempt: Yes
2022-10-07 15:25:36 -07:00
Paul Eggert
705bb09317 build: update submodules to latest
* src/common.h: Include <inttostr.h> since paxutils no longer does.
(STRINGIFY_BIGINT): New macro, copied from older paxutils.
(UINTMAX_STRSIZE_BOUND): New constant, also from older paxutils.
2022-09-10 18:54:21 -05:00
Paul Eggert
4ba281eca3 Fix bootstrap.conf when no paxutils
* bootstrap.conf: Don’t assume the paxutils subdirectory already
exists, when bootstrapping.
2022-09-10 18:54:21 -05:00
Paul Eggert
bc277c7069 Fix data loss when acting as filter
This bug was introduced by the recent lseek-related changes.
* src/delete.c (delete_archive_members):
* src/update.c (update_archive):
Copy the member if acting as a filter, rather than lseeking over
it, which is possible if stdin is a regular file.
* src/list.c (skim_file, skim_member):
* src/sparse.c (sparse_skim_file):
New functions, for copying when a filter.
* src/list.c (skip_file): Remove; replaced with skim_file.
All callers changed.
(skip_member): Reimplement in terms of skim_member.
* src/sparse.c (sparse_skip_file):
Remove; replaced with sparse_skim_file.  All callers changed.
* src/update.c (acting_as_filter): New static var.
(update_archive): Set it; this is like delete.c.
* tests/delete01.at (deleting a member after a big one):
* tests/delete02.at (deleting a member from stdin archive):
Also test filter case.
2022-09-03 18:23:11 -05:00
Paul Eggert
f8e14746d2 Fix --delete bug with short reads
* gnulib.modules: Add idx.
* src/common.h: Include idx.h.
* src/delete.c (move_archive): Don’t botch short reads.
2022-09-02 16:33:47 -05:00
Paul Eggert
35d9845d5d Do not diagnose same xattr file twice
* src/extract.c (set_xattr): Simplify, by having it do only
the mknodat and xattrs_xattrs_set, rather than also
trying to recover from failure.  Caller simplified too.
* tests/xattr07.at (xattrs: xattrs and --skip-old-files):
Adjust test to match fixed behavior.
2022-08-26 16:39:16 -05:00
Paul Eggert
0b74885e81 Fix bug with -x --xattr read-only files
Problem reported by Kevin Raymond in:
https://bugzilla.redhat.com/show_bug.cgi?id=1886540
* src/extract.c (open_output_file): If we already created the
empty file, do not open with O_EXCL, or with O_CREAT or O_TRUNC
for that matter.  Instead, use only O_NOFOLLOW to avoid some
races.  When estimating current mode, use openflag & O_EXCL rather
than overwriting_old_files.
(extract_file): Also invert S_IWUSR if it’s not set.
* tests/xattr08.at: New test.
* tests/Makefile.am, tests/testsuite.at: Add it.
2022-08-26 16:39:16 -05:00
Paul Eggert
258d1c44e5 Avoid quadratic behavior with delayed links
Do this by searching a hash table instead of a linked list.
Problem reported by Martin Dørum in https://mort.coffee/home/tar/
via Gavin Smith in:
https://lists.gnu.org/r/bug-tar/2022-07/msg00003.html
* src/extract.c: Include hash.h.
Improve performance a bit on non-birthtime hosts
(struct delayed_link.has_predecessor): New member.
(delayed_link_head): Remove, replacing with ...
(delayed_link_table): ... this new variable.  All uses
of linked list replaced with hash table.
(dl_hash, dl_compare): New functions for hash table.
(create_placeholder_file): Initialize has_predecessor.
(apply_delayed_link): New function, with body taken from
most of the old apply_delayed_link.
(apply_delayed_links): Use it.  Respect has_predecessor.
Don’t bother freeing as we are about to exit.
2022-08-15 00:07:39 -07:00
Paul Eggert
e49537dcdf Improve performance a bit on non-birthtime hosts
* src/extract.c (HAVE_BIRTHTIME, BIRTHTIME_EQ): New macros.
(struct delayed_link, create_placeholder_file, extract_link)
(apply_delayed_links): Avoid unnecessary work on platforms
like GNU/Linux that lack birthtime.
2022-08-15 00:07:39 -07:00
Paul Eggert
66be5a789e Avoid excess lseek etc.
* src/buffer.c, src/delete.c: Do not include system-ioctl.h.
* src/buffer.c (guess_seekable_archive): Remove.  This is now done
by get_archive_status, in a different way.
(get_archive_status): New function that gets archive_stat
unless remote, and sets seekable_archive etc.
(_open_archive): Prefer bool for boolean.
(_open_archive, new_volume): Get archive status consistently
by calling get_archive_status in both places.
* src/buffer.c (backspace_output):
* src/compare.c (verify_volume):
* src/delete.c (move_archive):
Let mtioseek worry about mtio.
* src/common.h (archive_stat): New global, replacing ar_dev and
ar_ino.  All uses changed.
* src/delete.c (move_archive): Check for integer overflow.
Also report overflow if the archive position would go negative.
* src/system.c: Include system-ioctl.h, for MTIOCTOP etc.
(mtioseek): New function, which also checks for integer overflow.
(sys_save_archive_dev_ino): Remove.
(archive_stat): Now
(sys_get_archive_stat): Also initialize mtioseekable_archive.
(sys_file_is_archive): Don’t return true if the archive is /dev/null
since it’s not a problem in that case.
(sys_detect_dev_null_output): Cache dev_null_stat.

doc: omit MS-DOS mentions in doc
It’s really FAT32 we’re worried about now, not MS-DOS.
And doschk is no longer a GNU program.
2022-08-15 00:07:39 -07:00
Paul Eggert
badd509078 Omit MS-DOS mentions in doc
It’s really FAT32 we’re worried about now, not MS-DOS.
And doschk is no longer a GNU program.
2022-08-15 00:07:39 -07:00
Paul Eggert
4e9e55fbf9 Work around GCC bug 106436
* src/tar.c (optloc_eq): Use ‘assume’ to pacify GCC bug.
2022-07-25 09:44:00 -07:00
Paul Eggert
fffc6149fd Avoid unlikely crash when xasprintf returns 0
Problem caught by GCC 12.
* src/tar.c (easprintf): New static function, which never returns
a null pointer.  All uses of xasprintf replaced by uses of this
function.
2022-07-25 09:44:00 -07:00
Paul Eggert
8ed180b03c Update .gitignore
* .gitignore: Redo for current sources.
2022-07-25 09:44:00 -07:00
Paul Eggert
131ceea7a5 Fix minor type confusion
* src/tar.c (decode_options): Don’t assign ‘false’ to a pointer.
2022-07-25 09:44:00 -07:00
Paul Eggert
4986147986 Work around GCC bug 106428
* src/names.c (read_next_name): Reword and clarify
to avoid GCC bug 106428.
2022-07-25 09:44:00 -07:00
Paul Eggert
e5aac38c80 Work around GCC bug 106427
* lib/wordsplit.c (coalesce_segment): Reword to avoid
GCC bug 106427.
2022-07-25 09:44:00 -07:00
Paul Eggert
5c4f8cadbd Adjust to Gnulib bootstrap revamp
* autogen.sh, autopull.sh, bootstrap-funclib.sh:
New files, copied from gnulib/top.
* bootstrap: Copy from gnulib/top/bootstrap (as opposed
to copying from gnulib/build-aux/bootstrap, as we used to).
* bootstrap.conf (bootstrap_post_pull_hook)
(bootstrap_post_import_hook): New functions.
Move commands into these functions as needed.
2022-07-24 11:46:56 -07:00
Paul Eggert
496cf61638 * doc/tar.1: Fix --xattrs-exclude typo. 2022-07-11 10:35:30 -05:00
Paul Eggert
d935dc7d1c Avoid EOVERFLOW problems in some symlink tests
* src/extract.c (is_directory_link): New arg ST.  Caller changed.
(is_directory_link, open_output_file):
Use readlinkat, not fstatat, to determine whether a string
names a symlink.  This avoids EOVERFLOW issues.
(extract_dir): Avoid duplicate calls to fstatat when
keep_directory_symlink_option && fstatat_flags == 0
and the file is a symlink to an existing file.
2022-06-13 17:03:49 -07:00
Paul Eggert
9f0e54ab2f Fix ‘make dist-xz’ bug
Problem reported by Pavel Raiskup in:
https://lists.gnu.org/r/bug-tar/2022-06/msg00014.html
* bootstrap.conf (copy_files): Also copy DISTFILES.
2022-06-13 16:50:11 -07:00
Paul Eggert
c7b69f05bc Fix commentary in new test case 2022-06-13 14:56:45 -07:00
Paul Eggert
4eb9d052b2 Fix doc -c typo
https://lists.gnu.org/r/bug-tar/2022-06/msg00006.html
* doc/tar.texi (Extended File Attributes): Fix typo.
2022-06-12 20:33:27 -07:00
Paul Eggert
5209d1dfe0 Update to current Autoconf & Gettext
* acinclude.m4, configure.ac:
Use AS_HELP_STRING, not AC_HELP_STRING.
* bootstrap: Sync from Gnulib.
* configure.ac: Require Autoconf 2.71 and Gettext 0.21.
Use AC_PROG_CC, not AC_PROG_CC_STDC.
Prefer AC_COMPILE_IFELSE to AC_TRY_COMPILE.
Use AC_CONFIG_FILES.
* gnulib.modules: Use gettext-h, not gettext.
2022-06-12 18:19:46 -07:00
Paul Eggert
b2ed3caefe build: update gnulib and paxutils submodules to latest 2022-06-12 18:19:46 -07:00
James Abbatiello
79d1ac38c1 tar: fix race condition
Problem reported in:
https://lists.gnu.org/r/bug-tar/2022-03/msg00000.html
* src/extract.c (make_directories): Retry the file creation as
long as the directory exists, regardless of whether tar itself
created the directory.
Copyright-paperwork-exempt: Yes
2022-06-10 18:26:37 -07:00
Paul Eggert
79a442d7b0 tar: fix race condition
Problem reported by James Abbatiello in:
https://lists.gnu.org/r/bug-tar/2022-03/msg00000.html
* src/extract.c (make_directories): Do not assume that when
mkdirat fails with errno == EEXIST that there is an existing file
that can be statted.  It could be a dangling symlink.  Instead,
wait until the end and stat it.
2022-06-10 18:26:37 -07:00
Paul Eggert
7a37621e5b test: new test extrac25.at
* tests/Makefile.am (TESTSUITE_AT): Add extrac25.at.
* tests/extrac25.at: New file.
* tests/testsuite.at: Include it.
2022-06-10 18:26:37 -07:00
Paul Eggert
c1027eb5ae Warn “file changed as we read it” less often
* src/create.c (dump_file0): Remove an fstatat call that is
unnecessary because the file wasn’t read so we can treat the first
fstatat as atomic.  Warn “file changed” when the file’s size,
mtime, user ID, group ID, or mode changes, instead of when the
file’s size or ctime changes.  Also, when such a change happens,
do not change exit status if --ignore-failed-read.  Finally, don’t
attempt to change atime back if it didn’t change.
2022-06-09 15:50:42 -07:00
Paul Eggert
bc7e758b27 Modernize NEWS hook to current Emacs 2022-06-09 15:50:42 -07:00
Paul Eggert
de72aa0cd6 doc: fix abrupt sentence in HTML
Typo reported by Jackson Dougherty in:
https://lists.gnu.org/r/bug-tar/2022-05/msg00000.html
* doc/tar.texi: Don’t assume that tex and info are the only two
formats.
2022-05-03 15:26:23 -07:00
Paul Eggert
eeda008a59 tar: revamp "file is the archive" diagnostic
* src/create.c (dump_file0): For clarity, change diagnostic
wording from "file is the archive; not dumped" to "archive cannot
contain itself; not dumped".  All test cases and documentation changed.
2022-02-15 17:42:51 -08:00
Sergey Poznyakoff
5c713540e3 Update copyright years 2022-01-02 10:25:29 +02:00
Paul Eggert
88ccec5d6c Copy COPYING and fdl.texi from upstream 2021-12-13 18:47:21 -08:00
Paul Eggert
399c08b3bf Format ` and ' more nicely in doc
Use @documentencoding and set txicodequoteundirected and
txicodequotebacktick to get nicer-looking quoting in the manual.
2021-12-13 18:47:21 -08:00
Paul Eggert
7f8fe36040 Don’t recommend delete=mtime
That stops working after the year 2242.
2021-12-13 18:47:21 -08:00
Paul Eggert
b846956c60 More reproducible tarball doc
* doc/tar.texi (PAX keywords): Improve discussion of how
to generate reproducible tarballs.
2021-12-13 12:43:58 -08:00
Sergey Poznyakoff
ac119c80e4 Raise version number 2021-12-13 16:14:26 +02:00
Paul Eggert
d43adc97d6 .gitignore maintenance (some automated) 2021-12-13 00:44:57 -08:00
Paul Eggert
1f73735ba0 bootstrap: copy from Gnulib 2021-12-13 00:44:57 -08:00
Paul Eggert
39cd8c6586 build: update gnulib and paxutils submodules to latest 2021-12-13 00:44:57 -08:00
Paul Eggert
84a08f530d Add NEWS items for changes since 1.34 2021-12-13 00:44:57 -08:00
Paul Eggert
c366383852 Omit unnecessary devmajor and devminor setting
* src/create.c (dump_file0): Omit redundant setting of devmajor
and devminor; start_header already does this.
2021-12-13 00:44:57 -08:00
Paul Eggert
738de9ecde Omit devmajor and devminor for non-special files
* src/create.c (start_header): Leave the devmajor and devminor
fields empty for files that are not character and block special
devices, even when the archive format is pax, ustar or v7.
This avoids generating irrelevant differences which helps with
reproducible builds, and is more compatible with what Solaris 10
tar does.
2021-12-13 00:44:57 -08:00
Paul Eggert
771ca7b686 Use ‘bool’ in create.c
* src/create.c: Prefer ‘bool’, ‘true’, ‘false’ for booleans.
2021-12-13 00:44:57 -08:00
Paul Eggert
8184adddab * gnulib.modules: Add year2038. 2021-10-13 14:46:10 -07:00
Paul Eggert
7868054c03 build: improve build-from-git for older GCCs
configure.ac: Bump GCC version from 4.6 to 11.2 when deciding whether
to default to enabling GCC warnings when --enable-gcc-warnings is not
specified, as older GCCs can generate too many false alarms.  From
a suggestion by Christian Schoenebeck.
2021-09-20 13:11:36 -07:00
Paul Eggert
4f3824743f Simplify wordsplit code a bit
These changes pacify gcc -Wanalyzer-null-dereference on x86-64 gcc
11.2.1 20210728 (Red Hat 11.2.1-1).
* lib/wordsplit.c (wsnode_tail): Remove unnecessary test.
(coalesce_segment): Coalesce calls to wsnode_len.
2021-09-19 07:36:36 -07:00
Paul Eggert
c5b86d7672 build: document WERROR_CFLAGS
* README-hacking: Mention 'make WERROR_CFLAGS='.
2021-09-18 08:16:04 -07:00
Paul Eggert
c0bf6a723b build: update paxutils submodule to latest 2021-09-17 12:57:14 -07:00
Paul Eggert
b44c612f5d build: update gnulib submodule to latest
* src/common.h (get_directory_entries):
Add _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE.
Problem found by gcc -Wsuggest-attribute=malloc and
current Gnulib.
2021-09-17 12:57:14 -07:00
Paul Eggert
066f7729e0 Pacify gcc -Warray-parameter
* src/common.h (code_timespec): Declare sbuf arg to be an array,
to be consistent with definition.
2021-09-17 12:57:14 -07:00
Paul Eggert
7958eb97e6 Fix null rereference if low-memory
* src/incremen.c (read_incr_db_01): Use xstrdup not strdup.
Problem found by gcc -Wanalyzer-possible-null-argument.
2021-09-17 12:57:14 -07:00
Sergey Poznyakoff
66262c10d1 Provide functions for manipulating arrays of extended attributes
* src/common.h (xheader_xattr_free,xheader_xattr_copy): Remove protos.
(xattr_map_init,xattr_map_copy)
(xattr_map_add,xattr_map_free): New protos.
* src/tar.h (xattr_map): New struct.
(tar_stat_info): Replace xattr_map_size and xattr_map with one
field: xattr_map.
* src/xattrs.c (XATTRS_PREFIX,XATTRS_PREFIX_LEN): New defines.
(xheader_xattr_init,xattr_map_init)
(xattr_map_free,xattr_map_add)
(xheader_xattr_add,xattr_map_copy): New functions.
All uses changed.
* src/create.c (start_header): Update to use struct xattr_map.
* src/extract.c: Update to use struct xattr_map.
* src/tar.c: Likewise.
* src/xheader.c (xheader_xattr_init,xheader_xattr_free)
(xheader_xattr_add,xheader_xattr_copy): Remove.
(xattr_coder,xattr_decoder): Use xattr_map_ functions.
2021-09-02 21:01:58 +03:00
Sergey Poznyakoff
a339f05cd2 Fix handling of extended header prefixes
* src/xheader.c (locate_handler): Recognize prefix keywords only
when followed by a dot.
(xattr_decoder): Use xmalloc/xstrdup instead of alloc
2021-08-28 17:40:57 +03:00
Sergey Poznyakoff
cc8f5f78b2 Handle invalid sparse entries in pax header
* src/sparse.c (pax_decode_header): Check if sp.offset+sp.numbytes
falls within the reported file size.  Check for possible integer
overflow.
2021-08-28 14:15:25 +03:00
Sergey Poznyakoff
193eb8f81d transform: fix replacement of particular pattern instance
This fixes handling of expressions like 's/s/@/2'

Fix suggested by Anthony Heading.

* src/transform.c (_single_transform_name_to_obstack): Avoid duplicating
initial prefix if replace is not needed.
2021-08-28 09:57:26 +03:00
Samanta Navarro
93082d6eb8 Fix crash on invalid command line argument
The copy_string_unquote function does not handle arguments which only
consist of a single quote. A string is only quoted if two quoting
characters exist.

How to reproduce:

tar --checkpoint-action exec=\"
2021-07-01 09:52:42 +03:00
Sergey Poznyakoff
e5bc23efcc Fix the use of options with trailing slash in files-from list
* src/names.c (read_name_from_file): Do not remove trailing slash
here, since name_buffer might contain an option (e.g. -C /).
(read_next_name): Remove trailing slash when we're sure we're dealing
with a file name.
See 163e96a0.
2021-07-01 09:38:35 +03:00
Sergey Poznyakoff
12d67f44de Minor fix
* scripts/backup-specs: Set standard paths for rsh (patch#9275).
2021-06-01 19:02:42 +03:00
Samanta Navarro
3730dddb3f Fix typos
Typos found with codespell
2021-06-01 18:54:07 +03:00
Samanta Navarro
8e82f367d4 Minor changes.
* src/xheader.c (decode_record): Fix format specification.
(xattr_encode_keyword): Allocate space for terminating \0.
2021-06-01 18:49:28 +03:00
Sergey Poznyakoff
66b59fccb1 Fix gendocs_template 2021-03-24 20:56:54 +02:00
Paul Eggert
91c031678d Tune for single-threaded tar
This takes advantage of recent optimizations in Gnulib
for single-threaded programs.
* configure.ac (GNULIB_EXCLUDE_SINGLE_THREAD)
(GNULIB_MBRTOWC_SINGLE_THREAD, GNULIB_REGEX_SINGLE_THREAD)
(GNULIB_WCHAR_SINGLE_LOCALE): Define.
2021-03-07 18:30:08 -08:00
Paul Eggert
5227d66b3c Port linking to AIX 7.1
* src/Makefile.am (LDADD): Remove, folding into tar_LDADD.
* src/Makefile.am (tar_LDADD), tests/Makefile.am (LDADD):
Add the libraries gnulib-tool currently recommends: LIB_ACL,
LIB_CLOCK_GETTIME, LIB_GETRANDOM, LIB_HARD_LOCALE, LIB_HAS_ACL,
LIB_MBRTOWC, LIB_SETLOCALE_NULL.  Otherwise, tar won’t link on AIX
7.1 with xlc because the -lpthread option is missing.
2021-03-07 18:30:08 -08:00
Paul Eggert
f51461d8be Port xattr-at.h to AIX 7.1
* lib/xattr-at.h: Include <errno.h> before testing whether
ENOATTR is defined.
2021-03-07 18:30:08 -08:00
Paul Eggert
6efa8de409 build: update gnulib submodule to latest 2021-03-07 18:30:08 -08:00
Sergey Poznyakoff
c67d223854 Fix building the online version of the manual.
* doc/Makefile.am (GENDOCS): Use the version from the tar repository.
(manual): Set the DISTRIN rendition.
(manual-rebuild): New goal.
* doc/README.manual: New file. Instructions for maintainers on how to
update web documentation.
* doc/gendocs.sh: A version from gnulib fixed as per
https://lists.gnu.org/archive/html/bug-gnulib/2021-03/msg00002.html.
* doc/gendocs_template: Updated version from gnulib.
* doc/intern.texi: Fix the use of UNREVISED.
* doc/tar.texi: Fix the use of GNUTAR.
2021-03-01 20:44:31 +02:00
Paul Eggert
55f2a0772e Fix unlikely uninitalized var bug with sparse file
* src/sparse.c (sparse_extract_file): Set *SIZE to
stat.st_size so that the caller does not use *SIZE
when uninitalized.  Problem found with GCC 10 and
--enable-gcc-warnings CFLAGS='-O2 -flto -fanalyzer'.
2021-03-01 00:18:44 -08:00
Paul Eggert
daf634c44e Pacify --enable-gcc-warnings -flto -fanalyzer
With GCC 10.2.1, ‘./configure --enable-gcc-warnings CFLAGS='-O2
-flto -fanalyzer' issued a false alarm about uninitialized
variable use.  Pacify GCC by using a variant of the code.
* src/buffer.c (zip_program): Omit last placeholder entry.
(n_zip_programs): New constant.
(find_zip_program): Use it instead of placeholder.
(first_decompress_program): Set *PSTATE to maximum value
if skipping the table.  This avoids confusing gcc -flto
into thinking *PSTATE is used uninitialized.
(next_decompress_program): Simplify now that *PSTATE is maximal
when skipping.
2021-03-01 00:18:44 -08:00
Paul Eggert
5f8f129415 Pacify clang for unreachable ‘return’
* src/sparse.c (sparse_scan_file_seek): Omit unreachable statement.
2021-03-01 00:18:44 -08:00
Paul Eggert
dc101747e8 Pacify clang char conversion
* src/create.c (to_chars): Pacify ‘clang’ about assigning
128 to ‘char’.
2021-03-01 00:18:44 -08:00
Paul Eggert
7fb1b6877f Omit most uses of ‘inline’
With today’s compilers ‘inline’ is typically not needed for
performance (at least the way GNU Tar uses it) and it gets in the
way of portability.
* configure.ac: Omit AC_C_INLINE; no longer needed here.
* lib/attr-xattr.in.h (setxattr, lsetxattr, fsetxattr, getxattr)
(lgetxattr, fgetxattr, listxattr, llistxattr, flistxattr):
* lib/wordsplit.c (skip_delim_internal, skip_delim)
(skip_delim_real, exptab_matches):
* src/delete.c (flush_file):
* src/extract.c (safe_dir_mode):
* src/misc.c (ptr_align):
Now just static, not static inline.
* lib/wordsplit.h (wordsplit_getwords): Remove; no longer used.
* src/common.h (name_more_files): Now COMMON_INLINE, not
extern inline - which is not portable according to C99,
the way we were using it.
2021-03-01 00:18:44 -08:00
Paul Eggert
f4e2411bf5 maint: port better to non-GCC compilers
This can be helpful in porting to compilers like Oracle Developer
Studio that support some but not all GCC attributes.
* lib/wordsplit.c (FALLTHROUGH): Remove; now done by attribute.h.
* lib/wordsplit.h (__WORDSPLIT_ATTRIBUTE_FORMAT): Remove;
all uses replaced by ATTRIBUTE_FORMAT.
* lib/wordsplit.h, src/buffer.c, src/common.h, src/compare.c:
* src/sparse.c, src/system.c, src/xheader.c:
Prefer ATTRIBUTE_FORMAT, MAYBE_UNUSED, _Noreturn, etc. to
__attribute__.
2021-03-01 00:18:44 -08:00
Paul Eggert
ace146f6a4 Update paxutils from upstream 2021-03-01 00:18:43 -08:00
Paul Eggert
df6de51574 maint: 1.34 announcement update
Hand-install 1.34 changes into master.
Evidently these changes were not pushed to savannah,
before I installed further changes today.
2021-02-27 18:40:49 -08:00
Paul Eggert
8378991cba maint: port to Fedora 33
Fedora 33 uses GCC 10.2.1, which is a bit pickier.
* configure.ac: Do not use -Wsystem-headers, as this
runs afoul of netdb.h on Fedora 33.
* gnulib.modules: Add ‘attribute’.
* lib/wordsplit.c (wsnode_new): Return the newly allocated
pointer instead of a boolean, to pacify GCC 10.2.1 which otherwise
complains about use of possibly-null pointers.  All uses changed.
* src/buffer.c (try_new_volume): Don’t assume find_next_block succeeds.
(_write_volume_label): Pacify GCC 10.2.1 with an ‘assume’, since
LABEL must be nonnull here.
* src/common.h (FALLTHROUGH): Remove; now in attribute.h.
Include attribute.h, for ATTRIBUTE_NONNULL.
* src/misc.c (assign_string_or_null): New function,
taking over the old role of assign_string.
(assign_string): Assume VALUE is non-null.
(assign_null): New function, taking over the old
role of assign_string when its VALUE was nonnull.
All callers of assign_string changed to use these functions.
(assign_string_n): Clear *STRING if VALUE is null,
to fix a potential double-free.
2021-02-27 16:42:49 -08:00
Paul Eggert
fa6d317bc7 Update gnulib and paxutils from upstream 2021-02-27 16:42:49 -08:00
Pavel Raiskup
beeb19f927 xattrs: fix capabilities root test
Related discussion in the Fedora pull-request:
https://src.fedoraproject.org/rpms/tar/pull-request/8

* tests/capabs_raw01.at: Newer systems (currently e.g. Fedora 34)
print getcap output in format CAP=VAL, not CAP+VAL.
2021-02-08 17:50:31 +02:00
Sergey Poznyakoff
553210d5ad Gracefully handle duplicate symlinks when extracting
If the archive being extracted contains multiple copies
of the same symlink, extract only the first of them and
skip the rest.  The use case is described in

https://lists.gnu.org/archive/html/bug-tar/2021-01/msg00026.html

The bug was introduced by 2d3396c3ea.

* src/extract.c (create_placeholder_file): If placeholder already
exists and is registered in the delayed_link list, return immediately.
2021-02-08 16:15:22 +02:00
Sergey Poznyakoff
ee2ec5ff19 Fix savannah bug #60002
Bug was introduced by commit 34d15af1.

* src/extract.c (prepare_to_extract): When extracting over pipe,
process only regular files.
* tests/extrac24.at: New test case.
* tests/Makefile.am: Add new test case.
* tests/testsuite.at: Likewise.
2021-02-04 16:51:38 +02:00
Sergey Poznyakoff
d9d4435692 Fix memory leak in read_header
Bug reported in https://savannah.gnu.org/bugs/?59897

* src/list.c (read_header): Don't return directly from the loop.
Instead set the status and break.  Return the status.  Free
next_long_name and next_long_link before returning.
2021-01-17 20:41:11 +02:00
Sergey Poznyakoff
2251317e3f Fix improper argument reference.
(bug introduced by 1ff0b63f)

* src/tar.c (parse_default_options): Pass args as is to the argp_parse.
2021-01-17 19:02:47 +02:00
Sergey Poznyakoff
972bebf07e Fix regression itroduced by 66162927.
* src/misc.c (chdir_arg): Initialize the abspath field to NULL.
(tar_getcdpath): Actually initialize the abspath field here.
2021-01-16 14:38:28 +02:00
Sergey Poznyakoff
8e2898ab11 Fixes in the delete_archive_members function
* src/delete.c (delete_archive_members): Clean-up the code. Avoid
entering the loop from the middle.  Free the memory allocated for
current_stat_info.
2021-01-14 09:00:12 +02:00
Sergey Poznyakoff
0b43ea2906 Use stdopen from gnulib
* gnulib.modules: Request stdopen
* lib/Makefile.am: Remove stdopen.c and stdopen.h
* lib/stdopen.c: Remove.
* lib/stdopen.h: Remove.
* src/tar.c: stdopen returns 0 on success.
2021-01-08 18:50:57 +02:00
Sergey Poznyakoff
e4d1edadef Actually prefer /dev/full over /dev/null as a replacement for stdin
* lib/stdopen.c (stdopen): Fix improper condition.
Avoid leaking extra file descriptor.
* src/tar.c (main): Set name of the stdout for diagnostics.
Bail out if stdopen fails.
2021-01-08 17:46:32 +02:00
Sergey Poznyakoff
0836a51147 Version 1.33
* NEWS: Update.
* configure.ac: Raise version number. Require automake 1.15 and
autoconf 2.64.
* gnulib: pull v0.1-4333-g30820c2
* src/names.c: Use hash_remove instead of the deprecated hash_delete.
2021-01-07 16:33:49 +02:00
Sergey Poznyakoff
afa743ac23 Update copyright years 2021-01-06 17:55:16 +02:00
Sergey Poznyakoff
01dd89c121 Fix typo in the manpage
* doc/tar.1: Fix description of the --newer option
2020-07-13 09:01:29 +03:00
Pavel Raiskup
1263f9bc1d Bugfix --sparse --diff mode
Originally reported in:
https://bugzilla.redhat.com/show_bug.cgi?id=1853469

* src/sparse.c (check_data_region): Only compare the part of buffer
really fed by safe_read(), not whole rdsize.
2020-07-10 09:06:42 +03:00
Sergey Poznyakoff
1ff0b63f48 Accept only position-sensitive (file-selection) options in file list files.
Using such options as -f, -z, etc. is senseless in the file list file
and bypasses the option consistency checks in decode_options.  Therefore,
only options related to file selection (a.k.a position-sensitive options)
are allowed in files.

* doc/tar.texi: Document changes.
* src/common.h (tar_args): Move from tar.c
(TAR_ARGS_INITIALIZER): New macro.
* src/names.c: Declare option group identifiers as an enum.
(names_parse_opt): Special handling for ARGP_KEY_ERROR.
(names_argp): Remove static qualifier.
(names_argp_children): Remove.
* src/tar.c: Declare option group identifiers as an enum.
(parse_opt): Special handling for ARGP_KEY_INIT.
(argp_children): New static variable.
(args): Remove static variable.
(more_options): Allow only options from names_argp.
(parse_default_options): Take a pointer to struct tar_args as argument.
Replace the loc member during the call to argp_parse and restore it
afterwards.
(decode_options): Use automatic variable for args.
2020-06-24 16:43:26 +03:00
Sergey Poznyakoff
34d15af170 Fix the logic of prepare_to_extract.
* src/extract.c (prepare_to_extract): Return true to proceed with
the extraction, and false to skip the current member.  If extracting
over a pipe, skip unlinking logic.
(extract_archive): Update accordingly.
2020-06-22 22:17:46 +03:00
Sergey Poznyakoff
63712973c7 Make sure link counting works for file names supplied with -T
* src/common.h (name_count): Remove extern.
(files_count): New enum.
(filename_args): New extern.
* src/names.c (name_count): Remove.
(files_count): New variable.
(name_add_name,name_add_file): Update filename_args.
* src/create.c (create_archive): Set trivial_link_count depending on
the filename_args.
2020-06-15 21:10:46 +03:00
Alfred M. Szmidt
d9ec6f04e2 Remove freemanuals.texi
* doc/Makefile.am (tar_TEXINFOS): Remove freemanuals.texi.
* doc/freemanuals.texi: Delete file.
* doc/tar.texi (Free Software Needs Free Documentation): Remove
appendix.
2020-06-10 21:56:54 +03:00
Sergey Poznyakoff
b5418cd393 Fix typo in docs 2020-06-03 10:55:51 +03:00
Sergey Poznyakoff
615732a804 Bugfix
* src/sparse.c (sparse_diff_file): Fix return value.
2020-06-03 09:31:41 +03:00
Paul Eggert
dd1a6bd37a tar: avoid read overrun
Problem reported by Timotej Kapus in:
https://lists.gnu.org/r/bug-tar/2020-05/msg00001.html
* src/transform.c (parse_transform_expr):
Diagnose ‘--transform='s'’ instead of continuing past '\0'.
2020-05-19 11:52:26 -07:00
Sergey Poznyakoff
41654f91f0 Fix handling of linked rename chains in incremental backups
* src/incremen.c: Change the meaning of the DIRF_RENAMED flag.  Now it
marks a directory which is the last one in a chain of renames.
Regular renamed directories are recognized by their orig member being
non-NULL.  Directories marked with DIRF_RENAMED start encoding of renames.
(procdir): Clear DIRF_RENAMED flag on directories which are origins for
renames.
(makedumpdir): Use the orig member to check if the directory is a
result of a rename.
(store_rename): Move the check for DIR_IS_RENAMED to the caller. Don't
clear the DIRF_RENAMED, it is not needed any more.

* tests/rename06.at: New test.
* tests/Makefile.am: Add rename06.at
* tests/testsuite.at: Likewise.
2020-02-15 11:09:18 +02:00
Sergey Poznyakoff
8d90723d30 Update copyright years 2020-02-08 13:21:13 +02:00
Sergey Poznyakoff
14d8fc718f Fix the --no-overwrite-dir option
Given this option, tar failed to preserve permissions of empty directories
and to create files under directories owned by the current user that did
not have the S_IWUSR bit set.

* src/extract.c (fd_chmod): Rename to fd_i_chmod.
(fd_chmod): New function.
(safe_dir_mode): New function.
(extract_dir): Special handling for existing directories in
--no-overwrite-dir mode.
* tests/extrac23.at: New file.
* tests/Makefile.am: Add new test case.
* tests/testsuite.at: Likewise.
2020-02-08 13:08:20 +02:00
Sergey Poznyakoff
883cc555df Wildcards in exclude-vcs-ignore mode should not match slash
* src/exclist.c (info_attach_exclist): Use the FNM_FILE_NAME flag.
2019-12-03 09:55:02 +02:00
Sergey Poznyakoff
e1005b385d Version 1.32.90 2019-11-27 18:51:58 +02:00
Sergey Poznyakoff
14f00a2c7a Testsuite fixes
* tests/sparse06.at: Skip the test if genfile is unable to create
sparse files.
* tests/sptrcreat.at: Likewise.
* tests/sptrdiff00.at: Likewise.
* tests/sptrdiff01.at: Likewise.
2019-11-25 08:01:04 +02:00
Sergey Poznyakoff
f122fc94a7 Another testsuite fix.
* tests/extrac11.at: Skip test if ulimit fails.
2019-11-14 07:58:17 +02:00
Sergey Poznyakoff
b31afe7cf0 Testsuite fix.
* tests/remfiles10.at: Don't expect EINVAL when trying to remove
CWD.
2019-11-13 08:37:41 +02:00
Sergey Poznyakoff
ea6f84dd40 Consistently use x2realloc for buffer reallocation
* src/create.c (create_archive): Use x2realloc
* src/names.c (copy_name)
(add_hierarchy_to_namelist): Use x2realloc
2019-08-25 14:41:17 +03:00
Sergey Poznyakoff
2d3396c3ea Fix extraction of symbolic links hardlinked to another symbolic links
* src/extract.c (create_placeholder_file): Take additional argument:
the delayed_link entry after which to the newly created one.
(extract_link): Create a placeholder file if the target link name
exists in the delayed_link list.
2019-08-18 23:40:54 +03:00
Sergey Poznyakoff
d70b8b3b39 Fix --delay-directory-restore on archives with reversed member ordering.
* src/extract.c (find_direct_ancestor): Remove useless test.
(delay_set_stat): If the file name being added is already in
the list, update stored data instead of creating a new entry.
This works for archives with reversed order of members.
* tests/extrac22.at: New testcase.
* tests/Makefile.am: Add new testcase.
* tests/testsuite.at: Include new testcase.
2019-04-11 13:45:32 +03:00
Sergey Poznyakoff
c445d99d4f Minor change
* scripts/backup.in: Fix typo
2019-03-28 14:01:58 +02:00
Sergey Poznyakoff
2c9730357f Typesetting fixes in the manpage
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-03-28 13:58:44 +02:00
Zachary Vance
ef0f882382 POSIX extended format headers do not include PID by default
The intent is to make binary-equivalent PAX archives easy to create.  If
POSIXLY_CORRECT is set, the POSIX standard default is used, which embeds
the pid.

* src/common.h (posixly_correct): New global.
* src/tar.c (decode_options): Detect the POSIXLY_CORRECT environment
variable.
* src/buffer.c (add_chunk_header): Change filenames of multipart files to
omit the pid.
* src/xheader.c (HEADER_TEMPLATE): New macro.
(xheader_xhdr_name, xheader_ghdr_name): Use HEADER_TEMPLATE to select
the template for the POSIX extended header name.
* doc/tar.texi: Document the change.

Signed-off-by: Zachary Vance <za3k@za3k.com>
2019-03-28 13:37:07 +02:00
Sergey Poznyakoff
66162927eb Check return value from xgetcwd
* src/misc.c (chdir_arg,tar_getcdpath): Check for non-NULL
return from xgetcwd. The function returns NULL for any
error originating from getcwd.
2019-03-03 09:02:20 +02:00
342 changed files with 10868 additions and 7876 deletions

44
.gitignore vendored
View File

@@ -1,32 +1,32 @@
*.a
*.[aios]
*.[gx]z
*.bz2
*.cache
*.diff
*.la
*.lo
*.o
*.orig
*.patch
*.rej
*.so
*DISTFILES
*~
.bootstrap
.deps
.emacs*
.libs
.gdbinit
ABOUT-NLS
ChangeLog
INSTALL
Make.rules
/.bootstrap
/ABOUT-NLS
/ChangeLog
/INSTALL
/Make.rules
/aclocal.m4
/build-aux/
/conf*
!/configure.ac
/gnu/
/m4/
/rmt/
/stamp-h1
Makefile
Makefile.in
aclocal.m4
autom4te.cache
build-aux
build-aux/
config.h
config.h.in
config.log
config.status
configure
gnu
libtool
m4
rmt
stamp-h1
TAGS

4
.gitmodules vendored
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

@@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

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

View File

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

View File

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

161
NEWS
View File

@@ -1,5 +1,160 @@
GNU tar NEWS - User visible changes. 2019-02-23
GNU tar NEWS - User visible changes. 2025-07-26
Please send GNU tar bug reports to <bug-tar@gnu.org>
version TBD
* New manual section "Reproducibility", for reproducible tarballs.
* New options: --set-mtime-command and --set-mtime-format
Both options are valid when archiving files.
** --set-mtime-command=COMMAND
For each FILE being archived, run "COMMAND FILE", parse its
output as time string and set mtime value of the archive member
from the result.
Unless --set-mtime-format is also used, the output is parsed
as argument to --mtime option (see GNU tar manual, chapter 4
"Date input formats".
** --set-mtime-format=FMT
Defines output format for the COMMAND set by the above option. If
used, command output will be parsed using strptime(3).
* Skip file or archive member if transformed name is empty
If applying file name transformations (--transform and
--strip-component options) to a file or member name results in an
empty string that file or member is skipped and a warning is printed.
The warning can be suppressed using the --warning=empty-transform
option.
* Bug fixes
** Fixed O(n^2) time complexity bug for large numbers of directories when
extracting with --delay-directory-restore or reading incremental archives.
** tar no longer uses alloca, fixing an unlikely stack overflow.
** When diagnosing invalid extended headers tar now quotes control characters.
** Transformations that change case (e.g., --transform='s/.*/\L&/')
now work correctly with multi-byte characters.
** --no-overwrite-dir no longer changes permissions of existing directories,
not even temporarily. This matches the documentation better and avoids
some permissions glitches.
** tar now works better in strict debugging environments that do not
allow pointer arithmetic to escape from a sub-element of an array.
* Performance improvements
** Sparse files are now read and written with larger blocksizes.
version 1.35 - Sergey Poznyakoff, 2023-07-18
* Fail when building GNU tar, if the platform supports 64-bit time_t
but the build uses only 32-bit time_t.
* Leave the devmajor and devminor fields empty (rather than zero) for
non-special files, as this is more compatible with traditional tar.
* Bug fixes
** Fix interaction of --update with --wildcards.
** When extracting archives into an empty directory, do not create
hard links to files outside that directory.
** Handle partial reads from regular files.
** Warn "file changed as we read it" less often.
Formerly, tar warned if the file's size or ctime changed.
However, this generated a false positive if tar read a file
while another process hard-linked to it, changing its ctime.
Now, tar warns if the file's size, mtime, user ID, group ID,
or mode changes. Although neither heuristic is perfect,
the new one should work better in practice.
** Fix --ignore-failed-read to ignore file-changed read errors
as far as exit status is concerned. You can now suppress file-changed
issues entirely with --ignore-failed-read --warning=no-file-changed.
** Fix --remove-files to not remove a file that changed while we read it.
** Fix --atime-preserve=replace to not fail if there was no need to replace,
either because we did not read the file, or the atime did not change.
** Fix race when creating a parent directory while another process is
also doing so.
** Fix handling of prefix keywords not followed by "." in pax headers.
** Fix handling of out-of-range sparse entries in pax headers.
** Fix handling of --transform='s/s/@/2'.
** Fix treatment of options ending in / in files-from list.
** Fix crash on 'tar --checkpoint-action exec=\"'.
** Fix low-memory crash when reading incremental dumps.
** Fix --exclude-vcs-ignores memory allocation misuse.
version 1.34 - Sergey Poznyakoff, 2021-02-13
* Fix extraction over pipe (savannah bug #60002)
* Fix memory leak in read_header (savannah bug #59897)
* Fix extraction when . and .. are unreadable
See https://lists.gnu.org/archive/html/bug-tar/2021-01/msg00012.html
* Gracefully handle duplicate symlinks when extracting
See https://lists.gnu.org/archive/html/bug-tar/2021-01/msg00026.html
* Re-initialize supplementary groups when switching to user privileges
version 1.33 - Sergey Poznyakoff, 2021-01-07
* POSIX extended format headers do not include PID by default
The intent is to make binary-equivalent PAX archives easy to create. If
POSIXLY_CORRECT is set, the POSIX standard default is used, which embeds
the pid.
* --delay-directory-restore works for archives with reversed member ordering
* Fix extraction of a symbolic link hardlinked to another symbolic link
* Wildcards in exclude-vcs-ignore mode don't match slash
* Fix the --no-overwrite-dir option
Given this option, previous versions of tar failed to preserve
permissions of empty directories and to create files under directories
owned by the current user that did not have the S_IWUSR bit set.
* Fix handling of chained renames in incremental backups
* Link counting works for file names supplied with -T
* Accept only position-sensitive (file-selection) options in file list files.
Using such options as -f, -z, etc. is senseless in a file list file and
bypasses option consistency checks in decode_options. Therefore,
only options related to file selection (a.k.a position-sensitive options)
are allowed in file list files.
version 1.32 - Sergey Poznyakoff, 2019-02-23
@@ -1687,7 +1842,7 @@ Versions 1.07 back to 1.00 by Jay Fenlason.
Copyright 1994-2019 Free Software Foundation, Inc.
Copyright 1994-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -1707,7 +1862,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Local variables:
mode: outline
paragraph-separate: "[ ]*$"
eval: (add-hook 'write-file-hooks 'time-stamp)
eval: (add-hook 'write-file-functions #'time-stamp nil t)
time-stamp-start: "changes. "
time-stamp-format: "%:y-%02m-%02d"
time-stamp-end: "\n"

2
README
View File

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

View File

@@ -3,39 +3,9 @@ This is GNU tar.
This is a *pre-release* version, and not ready for production use yet.
Please send comments and problem reports to <bug-tar@gnu.org>.
If you have taken the sources from CVS you will need the following
packages (or later) to build GNU tar. We don't make any extra effort
to accommodate older versions of these packages, so please make sure
that you have the latest stable version.
- Automake <http://www.gnu.org/software/automake/>
- Autoconf <http://www.gnu.org/software/autoconf/>
- Bison <http://www.gnu.org/software/bison/>
- Gettext <http://www.gnu.org/software/gettext/>
- Gzip <http://www.gnu.org/software/gzip/>
- M4 <http://www.gnu.org/software/m4/>
- Texinfo <http://www.gnu.org/software/texinfo>
- Wget <http://www.gnu.org/software/wget/>
As of this writing, the latest stable version of Gzip is 1.2.4 but we
suggest using test version 1.3.5 (or later, if one becomes available).
Valgrind <http://valgrind.org/> is also highly recommended, if
Valgrind supports your architecture.
Before building the package, run "bootstrap". It will obtain gnulib
and paxutils files from their Git repositories on Savannah. Then, it will
fetch the po files from tar page at Translation Project, and, finally, it
will start autoconfiguration process. Simply running bootstrap without
arguments should do in most cases.
Bootstrap reads its configuration from file bootstrap.conf located on the
top of tar source tree. Several options are provided that modify its
behavior. Run 'bootstrap --help' for a list.
Copyright 2001-2019 Free Software Foundation, Inc.
Copyright 2001-2025 Free Software Foundation, Inc.
This file is part of GNU tar.

View File

@@ -8,11 +8,22 @@ tar. We do not make any efforts to accommodate older versions of
these packages, so please make sure that you have the latest stable
version.
- Automake <http://www.gnu.org/software/automake/>
- Autoconf <http://www.gnu.org/software/autoconf/>
- Automake <http://www.gnu.org/software/automake/>
- Bison <http://www.gnu.org/software/bison/>
- M4 <http://www.gnu.org/software/m4/>
- Texinfo <http://www.gnu.org/software/texinfo>
- Gettext <http://www.gnu.org/software/gettext/>
- Git <http://git.or.cz>
- Gzip <http://www.gnu.org/software/gzip/>
- Texinfo <http://www.gnu.org/software/texinfo>
- Wget <http://www.gnu.org/software/wget/>
Up-to-date compilers and libraries are also recommended, for better
static checking. You may be able to use an older compiler by building
with 'make WERROR_CFLAGS='; if so, don't worry about its false alarms.
Valgrind <http://valgrind.org/> is also highly recommended, if
Valgrind supports your architecture.
* Bootstrapping
@@ -34,12 +45,26 @@ INSTALLATION).
Normally you will have to run bootstrap only once. However, if you
intend to hack on GNU tar, you might need to run it again later.
There are lots of options that you may find useful in this case.
See 'bootstrap --help' for a detailed list.
See './bootstrap --help' for a detailed list.
Bootstrapping obtains Gnulib and Paxutils files from their Git
repositories on Savannah. Then, it fetches translations from the
Translation Project, and, finally, it builds files useful for
configuration. Simply running ./bootstrap without arguments should do
in most cases.
The file bootstrap.conf contains bootstrapping configuration.
Several options are provided that modify its behavior.
Run './bootstrap --help' for a list.
To only fetch auxiliary files from the network, run './bootstrap --pull'.
To only generate files such as 'configure', without accessing the
network, run './bootstrap --gen'.
* Copyright information
Copyright 2007-2019 Free Software Foundation, Inc.
Copyright 2007-2025 Free Software Foundation, Inc.
This file is part of GNU tar.

7
THANKS
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-2019 Free Software Foundation, Inc.
Copyright 2003-2025 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-2019 Free Software Foundation, Inc.
dnl Copyright 2009-2025 Free Software Foundation, Inc.
dnl
dnl This file is part of GNU tar.
dnl
@@ -21,7 +21,7 @@ AC_DEFUN([TAR_COMPR_PROGRAM],[
m4_pushdef([tar_compr_define],translit($1,[a-z+-],[A-ZX_])[_PROGRAM])
m4_pushdef([tar_compr_var],[tar_cv_compressor_]translit($1,[+-],[x_]))
AC_ARG_WITH($1,
AC_HELP_STRING([--with-]$1[=PROG],
AS_HELP_STRING([--with-]$1[=PROG],
[use PROG as ]$1[ compressor program]),
[tar_compr_var=${withval}],
[tar_compr_var=m4_if($2,,$1,$2)])

1844
bootstrap

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Bootstrap configuration for GNU tar.
# Copyright 2006-2019 Free Software Foundation, Inc.
# Copyright 2006-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -20,10 +20,12 @@
source_base=gnu
gnulib_name=libgnu
# We don't need these modules, even though gnulib-tool mistakenly
# includes them because of gettext dependencies.
# We don't need these modules, even though gnulib-tool ordinarily
# includes them because of dependencies on the modules 'exclude' and 'regex'.
avoided_gnulib_modules='
--avoid=lock
--avoid=mbuiter
--avoid=mbuiterf
'
@@ -60,21 +62,19 @@ if [ -r .bootstrap ]; then
eval set -- "`sed 's/#.*$//;/^$/d' .bootstrap | tr '\n' ' '` $*"
fi
test -d m4 || mkdir m4
test -d $source_base || mkdir $source_base
bootstrap_post_pull_hook() {
mkdir -p m4 $source_base
git submodule init
git submodule update
}
test -f ChangeLog || cat > ChangeLog <<EOT
This file is a placeholder. It will be replaced with the actual ChangeLog
by make dist. Run make ChangeLog if you wish to create it earlier.
EOT
git submodule init
git submodule update
PAXUTILS=paxutils
# gnulib modules used by this package.
# getopt-gnu is for paxutils.
gnulib_modules="$avoided_gnulib_modules
`grep -h '^[^#]' gnulib.modules $PAXUTILS/gnulib.modules`
`grep -h '^[^#]' gnulib.modules`
getopt-gnu
"
# copy_files srcdir dstdir
@@ -100,27 +100,30 @@ copy_files() {
done
}
# Import from paxutils
copy_files ${PAXUTILS} .
copy_files ${PAXUTILS}/am m4
bootstrap_post_import_hook() {
echo "$0: Creating m4/paxutils.m4"
(echo "# This file is generated automatically. Please, do not edit."
echo "#"
echo "AC_DEFUN([${package}_PAXUTILS],["
cat ${PAXUTILS}/am/DISTFILES | sed '/^#/d;s/\(.*\)\.m4/pu_\1/' | tr a-z A-Z
echo "])") > ./m4/paxutils.m4
#FIXME ignorefile m4 paxutils.m4
test -f ChangeLog || cat > ChangeLog <<EOT
This file is a placeholder. It will be replaced with the actual ChangeLog
by make dist. Run make ChangeLog if you wish to create it earlier.
EOT
if [ -d rmt ]; then
:
else
mkdir rmt
fi
# Import from paxutils
copy_files ${PAXUTILS} .
copy_files ${PAXUTILS}/am m4
for dir in doc rmt lib tests
do
copy_files ${PAXUTILS}/$dir $dir
done
echo "$0: Creating m4/paxutils.m4"
(echo "# This file is generated automatically. Please, do not edit."
echo "#"
echo "AC_DEFUN([${package}_PAXUTILS],["
cat ${PAXUTILS}/am/DISTFILES | sed '/^#/d;s/\(.*\)\.m4/pu_\1/' | tr a-z A-Z
echo "])") > ./m4/paxutils.m4
#FIXME ignorefile m4 paxutils.m4
copy_files ${PAXUTILS}/paxlib lib pax
mkdir -p rmt
for dir in doc rmt lib tests; do
copy_files ${PAXUTILS}/$dir $dir
done
copy_files ${PAXUTILS}/paxlib lib pax
}

View File

@@ -1,6 +1,6 @@
# Configure template for GNU tar. -*- autoconf -*-
# Copyright 1991, 1994-2010, 2013-2019 Free Software Foundation, Inc.
# Copyright 1991, 1994-2010, 2013-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -17,46 +17,37 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_INIT([GNU tar], [1.32], [bug-tar@gnu.org])
AC_INIT([GNU tar], [1.35], [bug-tar@gnu.org])
AC_CONFIG_SRCDIR([src/tar.c])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
AC_PREREQ([2.63])
AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 dist-xz std-options silent-rules])
AC_PREREQ([2.71])
AM_INIT_AUTOMAKE([1.15 gnits tar-ustar dist-bzip2 dist-xz std-options silent-rules])
# Enable silent rules by default:
AM_SILENT_RULES([yes])
AC_PROG_CC_STDC
AC_PROG_CC
AC_EXEEXT
AC_PROG_RANLIB
AC_PROG_YACC
gl_EARLY
AC_CHECK_TOOLS([AR], [ar])
AC_SYS_LARGEFILE
AC_C_INLINE
AC_CHECK_HEADERS_ONCE(fcntl.h linux/fd.h memory.h net/errno.h \
sgtty.h string.h \
sys/param.h sys/device.h sys/gentape.h \
sys/inet.h sys/io/trioctl.h \
sys/mtio.h sys/time.h sys/tprintf.h sys/tape.h \
unistd.h locale.h)
AC_CHECK_HEADERS([sys/buf.h], [], [],
[#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif])
AC_CHECK_HEADERS_ONCE([linux/fd.h sys/mtio.h])
AC_HEADER_MAJOR
AC_MSG_CHECKING([for st_fstype string in struct stat])
AC_CACHE_VAL(diff_cv_st_fstype_string,
[AC_TRY_COMPILE([#include <sys/types.h>
#include <sys/stat.h>], [struct stat s; s.st_fstype[0] = 'x';],
diff_cv_st_fstype_string=yes,
diff_cv_st_fstype_string=no)])
[AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#include <sys/types.h>
#include <sys/stat.h>
]],
[[struct stat s; s.st_fstype[0] = 'x';]])],
[diff_cv_st_fstype_string=yes],
[diff_cv_st_fstype_string=no])])
AC_MSG_RESULT($diff_cv_st_fstype_string)
if test $diff_cv_st_fstype_string = yes; then
AC_DEFINE(HAVE_ST_FSTYPE_STRING, 1,
@@ -91,28 +82,45 @@ AC_TYPE_MODE_T
AC_TYPE_PID_T
AC_TYPE_OFF_T
AC_TYPE_UID_T
AC_CHECK_TYPE(major_t, , AC_DEFINE(major_t, int,
[Type of major device numbers.]))
AC_CHECK_TYPE(minor_t, , AC_DEFINE(minor_t, int,
[Type of minor device numbers.]))
AC_CHECK_TYPE(dev_t, unsigned)
AC_CHECK_TYPE(ino_t, unsigned)
# Taken from GNU/Linux, and should be good enough on platforms
# lacking these types.
AC_CHECK_TYPE([dev_t], [unsigned long long int])
AC_CHECK_TYPE([ino_t], [unsigned long long int])
# Taken from GNU/Linux, and should be good enough on platforms
# lacking these types.
AC_CHECK_TYPE([major_t], [unsigned int])
AC_CHECK_TYPE([minor_t], [unsigned int])
gt_TYPE_SSIZE_T
# gnulib modules
gl_INIT
if test $ac_cv_lib_error_at_line = no; then
# This means that the error() function is not present in libc, so
# the one from gnulib will be used instead. This function precedes
AC_DEFINE([GNULIB_EXCLUDE_SINGLE_THREAD], [1],
[Define if all programs in this package call functions of the Gnulib
'exclude' module only from a single thread.])
AC_DEFINE([GNULIB_MBRTOWC_SINGLE_THREAD], [1],
[Define if all programs in this package call functions of the Gnulib
'mbtowc' module only from a single thread.])
AC_DEFINE([GNULIB_REGEX_SINGLE_THREAD], [1],
[Define if all programs in this package call functions of the Gnulib
'regex' module only from a single thread.])
AC_DEFINE([GNULIB_WCHAR_SINGLE_LOCALE], [1],
[Define if all programs in this package call locale-sensitive functions
like mbrtowc only after setting the locale, and never change the
locale once set.])
if test $COMPILE_ERROR_C = 1; then
# This means that Gnulib's 'error' function will be used. It precedes
# error messages it prints with the program name as returned by getprogname()
# call, instead of using the name set by set_program_name.
# Install workaround.
AC_DEFINE([ENABLE_ERROR_PRINT_PROGNAME],[1],
[Enable the use of error_print_progname to print program name with error messages.
See comment to function tar_print_progname in src/tar.c])
fi
fi
# paxutils modules
tar_PAXUTILS
@@ -145,7 +153,7 @@ AC_ARG_ENABLE([gcc-warnings],
gl_gcc_warnings=$enableval],
[gl_gcc_warnings=no
if test -d "$srcdir"/.git; then
gl_GCC_VERSION_IFELSE([4], [6], [gl_gcc_warnings=yes])
gl_GCC_VERSION_IFELSE([11], [2], [gl_gcc_warnings=yes])
fi]
)
@@ -171,12 +179,11 @@ if test "$gl_gcc_warnings" = yes; then
done
gl_WARN_ADD([-Wno-sign-compare]) # Too many warnings for now
gl_WARN_ADD([-Wno-type-limits]) # It's OK to optimize based on types.
gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
gl_WARN_ADD([-Wno-format-nonliteral])
gl_WARN_ADD([-fdiagnostics-show-option])
gl_WARN_ADD([-funit-at-a-time])
AC_SUBST([WARN_CFLAGS])
@@ -218,13 +225,7 @@ fi
TAR_HEADERS_ATTR_XATTR_H
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync lstat mkfifo readlink symlink])
AC_CHECK_DECLS([getgrgid],,, [#include <grp.h>])
AC_CHECK_DECLS([getpwuid],,, [#include <pwd.h>])
AC_CHECK_DECLS([time],,, [#include <time.h>])
AC_REPLACE_FUNCS(waitpid)
AC_CHECK_FUNCS_ONCE([fchmod fchown fsync mkfifo waitpid])
AC_ARG_VAR([RSH], [Configure absolute path to default remote shell binary])
AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
@@ -248,9 +249,7 @@ AC_CACHE_CHECK(for remote shell, tar_cv_path_RSH,
fi
done
fi])
if test $tar_cv_path_RSH = no; then
AC_CHECK_HEADERS(netdb.h)
else
if test $tar_cv_path_RSH != "no"; then
AC_DEFINE_UNQUOTED(REMOTE_SHELL, "$tar_cv_path_RSH",
[Define to the full path of your rsh, if any.])
fi
@@ -344,7 +343,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_QUOTING_STYLE, $DEFAULT_QUOTING_STYLE,
# Iconv
AM_ICONV
AC_CHECK_HEADERS(iconv.h)
AC_CHECK_HEADERS_ONCE([iconv.h])
AC_CHECK_TYPE(iconv_t,:,
AC_DEFINE(iconv_t, int,
[Conversion descriptor type]),
@@ -356,7 +355,7 @@ AC_CHECK_TYPE(iconv_t,:,
# Gettext.
AM_GNU_GETTEXT([external], [need-formatstring-macros])
AM_GNU_GETTEXT_VERSION([0.16])
AM_GNU_GETTEXT_VERSION([0.21])
# Initialize the test suite.
AC_CONFIG_TESTDIR(tests)
@@ -366,8 +365,8 @@ AM_MISSING_PROG([AUTOM4TE], [autom4te])
AC_SUBST(BACKUP_LIBEXEC_SCRIPTS)
AC_SUBST(BACKUP_SBIN_SCRIPTS)
AC_ARG_ENABLE(backup-scripts,
AC_HELP_STRING([--enable-backup-scripts],
[Create and install backup and restore scripts]),
AS_HELP_STRING([--enable-backup-scripts],
[Create and install backup and restore scripts]),
[case $enableval in
yes) BACKUP_LIBEXEC_SCRIPTS='$(BACKUP_LIBEXEC_SCRIPTS_LIST)'
BACKUP_SBIN_SCRIPTS='$(BACKUP_SBIN_SCRIPTS_LIST)'
@@ -381,7 +380,7 @@ else
BACKUP_SED_COND='/^\#IF_DATE_FORMAT_OK/,/^\#ELSE_DATE_FORMAT_OK/d;/^\#ENDIF_DATE_FORMAT_OK/d'
fi
AC_OUTPUT([Makefile\
AC_CONFIG_FILES([Makefile\
doc/Makefile\
gnu/Makefile\
lib/Makefile\
@@ -389,3 +388,4 @@ AC_OUTPUT([Makefile\
scripts/Makefile\
rmt/Makefile\
src/Makefile])
AC_OUTPUT

View File

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

View File

@@ -1,6 +1,6 @@
# Makefile for GNU tar documentation.
# Copyright 1994-2019 Free Software Foundation, Inc.
# Copyright 1994-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -22,7 +22,6 @@ tar_TEXINFOS = \
dumpdir.texi\
tar-snapshot-edit.texi\
fdl.texi\
freemanuals.texi\
genfile.texi\
header.texi\
intern.texi\
@@ -139,9 +138,9 @@ check-docs:
clean-local:
rm -rf manual
GENDOCS=gendocs.sh
GENDOCS=$(srcdir)/gendocs.sh
TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -E
TEXI2DVI=texi2dvi -E
# Make sure you set TEXINPUTS
# Usual value is:
@@ -149,5 +148,8 @@ TEXI2DVI=texi2dvi -t '@set $(RENDITION)' -E
manual:
TEXINPUTS=$(srcdir):$(top_srcdir)/build-tex:$(TEXINPUTS) \
MAKEINFO="$(MAKEINFO) $(MAKEINFOFLAGS)" \
TEXI2DVI="$(TEXI2DVI) -t @finalout" \
TEXI2DVI="$(TEXI2DVI) -t '@set DISTRIB' -t @finalout" \
$(GENDOCS) --texi2html tar 'GNU tar manual'
manual-rebuild: clean-local manual

91
doc/README.manual Normal file
View File

@@ -0,0 +1,91 @@
* Overview
This file is a short instruction for maintainers on how to create and
publish the online version of the Tar Manual.
In the sections below we assume that the tar project has been properly
cloned from the git repo, bootstrapped and configured. We also assume
that top-level directory of the project is the current local directory.
* Creating the web manual
To create the online version of the documentation, run
make -C doc manual-rebuild
This will create the directory doc/manual populated with the tar
documentation files in various formats. If the doc/manual directory
already exists, it will be removed prior to rebuilding.
The command produces very copious output. We advise you to examine it
closely to make sure no error messages slip your attention.
For the completeness sake, there are two more Makefile goals related
to the online manual:
** make -C doc clean-local
Removes the doc/manual directory, if it exists.
** make -C doc manual
Builds the doc/manual, unless it already exists.
* CVS Repository
The online tar manual[1] is a part of tar web pages[2] and is
traditionally maintained in the CVS repository[3]. To publish the
generated documentation, you will need first to check out tar web
pages from the CVS. To do so, run
cvs -z3 -d:ext:<username>@cvs.savannah.gnu.org:/web/tar co tar
where <username> is your user name on Savannah. For the rest of this
document we will assume that the checked out version of the tar web
pages resides in the ~/websrc/tar directory.
If you have already checked out the web pages, be sure to update them
before publishing:
cd ~/websrc/tar
cvs update
* Publishing
To publish the created manual, change to the tar top-level directory
and run:
rsync -avz --exclude CVS --delete manual ~/websrc/tar
This will synchronize the newly created manual pages with the content
of the CVS sandbox. Then, change to the ~/websrc/tar directory and
schedule any removed files for removal and any new files for addition
to the repository:
cvs diff --brief 2>&1 | sed -n 's/.*cannot find //p' | xargs cvs rm
cvs diff --brief 2>&1 | sed -n 's/^? //p' | xargs cvs add
Then commit your changes:
cvs commit
Once the changes are committed to CVS a job is scheduled on the server,
which synchronizes them with the content of the directory served by
the httpd daemon. Normally such synchronization happens within
several seconds from the commit.
For more information about CVS, please see its documentation[4].
* References
[1] https://www.gnu.org/software/tar/manual/
[2] https://www.gnu.org/software/tar/
[3] https://web.cvs.savannah.gnu.org/viewvc/tar/
[4] https://www.nongnu.org/cvs/#documentation
Local Variables:
mode: outline
paragraph-separate: "[ ]*$"
version-control: never
End:

View File

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

View File

@@ -5,8 +5,9 @@
@c hence no sectioning command or @node.
@display
Copyright @copyright{} 2000-2019 Free Software Foundation, Inc.
@uref{http://fsf.org/}
Copyright @copyright{} 2000--2002, 2007--2008, 2022 Free Software
Foundation, Inc.
@uref{https://fsf.org/}
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -97,7 +98,7 @@ format, SGML or XML using a publicly available
DTD, and standard-conforming simple HTML,
PostScript or PDF designed for human modification. Examples
of transparent image formats include PNG, XCF and
JPG. Opaque formats include proprietary formats that can be
JPG@. Opaque formats include proprietary formats that can be
read and edited only by proprietary word processors, SGML or
XML for which the DTD and/or processing tools are
not generally available, and the machine-generated HTML,
@@ -414,7 +415,7 @@ The Free Software Foundation may publish new, revised versions
of the GNU Free Documentation License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns. See
@uref{http://www.gnu.org/copyleft/}.
@uref{https://www.gnu.org/licenses/}.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
@@ -481,7 +482,7 @@ license notices just after the title page:
@end smallexample
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the ``with@dots{}Texts.'' line with this:
replace the ``with@dots{}Texts.''@: line with this:
@smallexample
@group
@@ -503,4 +504,3 @@ to permit their use in free software.
@c Local Variables:
@c ispell-local-pdict: "ispell-dict"
@c End:

View File

@@ -1,89 +0,0 @@
@cindex free documentation
The biggest deficiency in the free software community today is not in
the software---it is the lack of good free documentation that we can
include with the free software. Many of our most important
programs do not come with free reference manuals and free introductory
texts. Documentation is an essential part of any software package;
when an important free software package does not come with a free
manual and a free tutorial, that is a major gap. We have many such
gaps today.
Consider Perl, for instance. The tutorial manuals that people
normally use are non-free. How did this come about? Because the
authors of those manuals published them with restrictive terms---no
copying, no modification, source files not available---which exclude
them from the free software world.
That wasn't the first time this sort of thing happened, and it was far
from the last. Many times we have heard a GNU user eagerly describe a
manual that he is writing, his intended contribution to the community,
only to learn that he had ruined everything by signing a publication
contract to make it non-free.
Free documentation, like free software, is a matter of freedom, not
price. The problem with the non-free manual is not that publishers
charge a price for printed copies---that in itself is fine. (The Free
Software Foundation sells printed copies of manuals, too.) The
problem is the restrictions on the use of the manual. Free manuals
are available in source code form, and give you permission to copy and
modify. Non-free manuals do not allow this.
The criteria of freedom for a free manual are roughly the same as for
free software. Redistribution (including the normal kinds of
commercial redistribution) must be permitted, so that the manual can
accompany every copy of the program, both on-line and on paper.
Permission for modification of the technical content is crucial too.
When people modify the software, adding or changing features, if they
are conscientious they will change the manual too---so they can
provide accurate and clear documentation for the modified program. A
manual that leaves you no choice but to write a new manual to document
a changed version of the program is not really available to our
community.
Some kinds of limits on the way modification is handled are
acceptable. For example, requirements to preserve the original
author's copyright notice, the distribution terms, or the list of
authors, are ok. It is also no problem to require modified versions
to include notice that they were modified. Even entire sections that
may not be deleted or changed are acceptable, as long as they deal
with nontechnical topics (like this one). These kinds of restrictions
are acceptable because they don't obstruct the community's normal use
of the manual.
However, it must be possible to modify all the @emph{technical}
content of the manual, and then distribute the result in all the usual
media, through all the usual channels. Otherwise, the restrictions
obstruct the use of the manual, it is not free, and we need another
manual to replace it.
Please spread the word about this issue. Our community continues to
lose manuals to proprietary publishing. If we spread the word that
free software needs free reference manuals and free tutorials, perhaps
the next person who wants to contribute by writing documentation will
realize, before it is too late, that only free manuals contribute to
the free software community.
If you are writing documentation, please insist on publishing it under
the GNU Free Documentation License or another free documentation
license. Remember that this decision requires your approval---you
don't have to let the publisher decide. Some commercial publishers
will use a free license if you insist, but they will not propose the
option; it is up to you to raise the issue and say firmly that this is
what you want. If the publisher you are dealing with refuses, please
try other publishers. If you're not sure whether a proposed license
is free, write to @email{licensing@@gnu.org}.
You can encourage commercial publishers to sell more free, copylefted
manuals and tutorials by buying them, and particularly by buying
copies from the publishers that paid for their writing or for major
improvements. Meanwhile, try to avoid buying non-free documentation
at all. Check the distribution terms of a manual before you buy it,
and insist that whoever seeks your business must respect your freedom.
Check the history of the book, and try reward the publishers that have
paid or pay the authors to work on it.
The Free Software Foundation maintains a list of free documentation
published by other publishers, at
@url{http://www.fsf.org/doc/other-free-books.html}.

512
doc/gendocs.sh Executable file
View File

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

174
doc/gendocs_template Executable file → Normal file
View File

@@ -1,125 +1,101 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- $Id: gendocs_template,v 1.5 2007/10/30 14:58:52 gray Exp $ -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<!--#include virtual="/server/header.html" -->
<!-- Parent-Version: 1.78 -->
<head>
<title>%%TITLE%% - GNU Project - Free Software Foundation (FSF)</title>
<meta http-equiv="content-type" content='text/html; charset=utf-8' />
<link rel="stylesheet" type="text/css" href="/gnu.css" />
<link rev="made" href="mailto:gray@gnu.org" />
<link rel="icon" type="image/png" href="/graphics/gnu-head-icon.png" />
</head>
<!--
Copyright (C) 2006-2025 Free Software Foundation, Inc.
<!-- This document is in XML, and xhtml 1.0 -->
<!-- Please make sure to properly nest your tags -->
<!-- and ensure that your final document validates -->
<!-- consistent with W3C xhtml 1.0 and CSS standards -->
<!-- See validator.w3.org -->
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
-->
<body>
<h3>%%TITLE%%</h3>
<title>%%TITLE%% - GNU Project - Free Software Foundation</title>
<!--#include virtual="/server/banner.html" -->
<h2>%%TITLE%%</h2>
<address>Free Software Foundation</address>
<address>last updated %%DATE%%</address>
<p>
<a href="/graphics/gnu-head.jpg">
<img src="/graphics/gnu-head-sm.jpg"
alt=" [image of the head of a GNU] "
width="129" height="122" />
</a>
</p>
<hr />
<p>The manual for %%PACKAGE%% is available in the following formats:</p>
<p>This manual (%%PACKAGE%%) is available in the following formats:</p>
<ul>
<li><a href="%%PACKAGE%%.html">HTML
(%%HTML_MONO_SIZE%%K bytes)</a> - entirely on one web page.</li>
<li><a href="html_node/index.html">HTML</a> - with one web page per
node.</li>
<li><a href="%%PACKAGE%%.html">HTML
(%%HTML_MONO_SIZE%%K bytes)</a> - entirely on one web page.</li>
<li><a href="html_node/index.html">HTML</a> - with one web page per
node.</li>
%%IF HTML_SECTION%%
<li><a href="html_section/index.html">HTML</a> - with one web page per
section.</li>
<li><a href="html_section/index.html">HTML</a> - with one web page per
section.</li>
%%ENDIF HTML_SECTION%%
%%IF HTML_CHAPTER%%
<li><a href="html_chapter/index.html">HTML</a> - with one web page per
chapter.</li>
<li><a href="html_chapter/index.html">HTML</a> - with one web page per
chapter.</li>
%%ENDIF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.html.gz">HTML compressed
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
one web page.</li>
<li><a href="%%PACKAGE%%.html_node.tar.gz">HTML compressed
(%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per node.</li>
<li><a href="%%PACKAGE%%.html.gz">HTML compressed
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
one web page.</li>
<li><a href="%%PACKAGE%%.html_node.tar.gz">HTML compressed
(%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per node.</li>
%%IF HTML_SECTION%%
<li><a href="%%PACKAGE%%.html_section.tar.gz">HTML compressed
(%%HTML_SECTION_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per section.</li>
<li><a href="%%PACKAGE%%.html_section.tar.gz">HTML compressed
(%%HTML_SECTION_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per section.</li>
%%ENDIF HTML_SECTION%%
%%IF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.html_chapter.tar.gz">HTML compressed
(%%HTML_CHAPTER_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per chapter.</li>
<li><a href="%%PACKAGE%%.html_chapter.tar.gz">HTML compressed
(%%HTML_CHAPTER_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per chapter.</li>
%%ENDIF HTML_CHAPTER%%
<li><a href="%%PACKAGE%%.info.tar.gz">Info document
(%%INFO_TGZ_SIZE%%K characters gzipped tar file)</a>.</li>
<li><a href="%%PACKAGE%%.txt">ASCII text
(%%ASCII_SIZE%%K characters)</a>.</li>
<li><a href="%%PACKAGE%%.txt.gz">ASCII text compressed
(%%ASCII_GZ_SIZE%%K gzipped characters)</a>.</li>
<li><a href="%%PACKAGE%%.dvi.gz">TeX dvi file
(%%DVI_GZ_SIZE%%K characters gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.ps.gz">PostScript file
(%%PS_GZ_SIZE%%K characters gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.pdf">PDF file
(%%PDF_SIZE%%K characters)</a>.</li>
<li><a href="%%PACKAGE%%.texi.tar.gz">Texinfo source
(%%TEXI_TGZ_SIZE%%K characters gzipped tar file)</a></li>
<li><a href="%%PACKAGE%%.info.tar.gz">Info document
(%%INFO_TGZ_SIZE%%K bytes gzipped tar file)</a>.</li>
<li><a href="%%PACKAGE%%.txt">ASCII text
(%%ASCII_SIZE%%K bytes)</a>.</li>
<li><a href="%%PACKAGE%%.txt.gz">ASCII text compressed
(%%ASCII_GZ_SIZE%%K bytes gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.dvi.gz">TeX dvi file
(%%DVI_GZ_SIZE%%K bytes gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.pdf">PDF file
(%%PDF_SIZE%%K bytes)</a>.</li>
<li><a href="%%PACKAGE%%.texi.tar.gz">Texinfo source
(%%TEXI_TGZ_SIZE%%K bytes gzipped tar file).</a></li>
</ul>
<p>(This page generated by the <a
href="%%SCRIPTURL%%">%%SCRIPTNAME%%</a> script.)
</p>
<p>You can <a href="https://shop.fsf.org/">buy printed copies of
some manuals</a> (among other items) from the Free Software Foundation;
this helps support FSF activities.</p>
<p>
<a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0!" height="31" width="88" /></a>
</p>
<p>(This page generated by the <a href="%%SCRIPTURL%%">%%SCRIPTNAME%%
script</a>.)</p>
<div class="copyright">
<p>
Return to the <a href="/home.html">GNU Project home page</a>.
</p>
<!-- If needed, change the copyright block at the bottom. In general,
all pages on the GNU web server should have the section about
verbatim copying. Please do NOT remove this without talking
with the webmasters first.
Please make sure the copyright date is consistent with the document
and that it is like this: "2001, 2002", not this: "2001-2002". -->
</div><!-- for id="content", starts in the include above -->
<!--#include virtual="/server/footer.html" -->
<div id="footer">
<div class="unprintable">
<p>
Please send FSF &amp; GNU inquiries to
<a href="mailto:gnu@gnu.org"><em>gnu@gnu.org</em></a>.
There are also <a href="/home.html#ContactInfo">other ways to contact</a>
the FSF.
<br />
Please send broken links and other corrections (or suggestions) to
<a href="mailto:webmasters@gnu.org"><em>webmasters@gnu.org</em></a>.
</p>
<p>
Copyright 2004-2019 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02111, USA
<br />
Verbatim copying and distribution of this entire article is
permitted in any medium, provided this notice is preserved.
</p>
<p>
Updated:
<!-- timestamp start -->
$Date: 2007/10/30 14:58:52 $ $Author: gray $
<!-- timestamp end -->
</p>
<p>Please send general FSF &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, 2025 Free Software Foundation, Inc.</p>
<p>This page is licensed under a <a rel="license"
href="https://creativecommons.org/licenses/by-nd/3.0/us/">Creative
Commons Attribution-NoDerivs 3.0 United States License</a>.</p>
<!--#include virtual="/server/bottom-notes.html" -->
</div>
</div>
</body>
</html>

View File

@@ -1,5 +1,5 @@
@c This is part of the paxutils manual.
@c Copyright (C) 2006-2019 Free Software Foundation, Inc.
@c Copyright (C) 2006--2025 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.1 or any later version
@c published by the Free Software Foundation.
@@ -13,7 +13,7 @@
@node Standard
@unnumberedsec Basic Tar Format
@UNREVISED
@UNREVISED{}
While an archive may contain many files, the archive itself is a
single ordinary file. Like any other file, an archive file can be
@@ -51,7 +51,10 @@ of the file. At the end of the archive file there are two 512-byte blocks
filled with binary zeros as an end-of-file marker. A reasonable system
should write such end-of-file marker at the end of an archive, but
must not assume that such a block exists when reading an archive. In
particular @GNUTAR{} always issues a warning if it does not encounter it.
particular, @GNUTAR{} does not treat missing end-of-file marker as an
error and silently ignores the fact. You can instruct it to issue
a warning, however, by using the @option{--warning=missing-zero-blocks}
option (@pxref{General Warnings, missing-zero-blocks}).
The blocks may be @dfn{blocked} for physical I/O operations.
Each record of @var{n} blocks (where @var{n} is set by the
@@ -111,8 +114,9 @@ The @code{uid} and @code{gid} fields are the numeric user and group
not support numeric user or group @acronym{ID}s, these fields should
be ignored.
The @code{size} field is the size of the file in bytes; linked files
are archived with this field specified as zero.
The @code{size} field is the size of the file in bytes; for archive
members that are symbolic or hard links to another file, this field
is specified as zero.
The @code{mtime} field represents the data modification time of the file at
the time it was archived. It represents the integer number of
@@ -123,7 +127,7 @@ the simple sum of all bytes in the header block. Each 8-bit
byte in the header is added to an unsigned integer, initialized to
zero, the precision of which shall be no less than seventeen bits.
When calculating the checksum, the @code{chksum} field is treated as
if it were all blanks.
if it were filled with spaces (ASCII 32).
The @code{typeflag} field specifies the type of file archived. If a
particular implementation does not recognize or permit the specified
@@ -266,7 +270,7 @@ IEEE Std 1003.2-1992, pages 380-388 (section 4.48) and pages 936-940
@node Extensions
@unnumberedsec @acronym{GNU} Extensions to the Archive Format
@UNREVISED
@UNREVISED{}
The @acronym{GNU} format uses additional file types to describe new types of
files in an archive. These are listed below.

View File

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

View File

@@ -1,5 +1,5 @@
@c This is part of the GNU tar manual.
@c Copyright (C) 2017-2019 Free Software Foundation, Inc.
@c Copyright (C) 2017--2025 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.3 or any later version
@c published by the Free Software Foundation.

View File

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

View File

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

View File

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

View File

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

181
doc/tar.1
View File

@@ -1,5 +1,4 @@
.\" This file is part of GNU tar. -*- nroff -*-
.\" Copyright 2013-2019 Free Software Foundation, Inc.
.\"
.\" GNU tar is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@@ -13,7 +12,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.TH TAR 1 "February 4, 2019" "TAR" "GNU TAR Manual"
.TH TAR 1 "January 1, 2025" "TAR" "GNU TAR Manual"
.SH NAME
tar \- an archiving utility
.SH SYNOPSIS
@@ -21,23 +20,21 @@ tar \- an archiving utility
\fBtar\fR {\fBA\fR|\fBc\fR|\fBd\fR|\fBr\fR|\fBt\fR|\fBu\fR|\fBx\fR}\
[\fBGnSkUWOmpsMBiajJzZhPlRvwo\fR] [\fIARG\fR...]
.SS UNIX-style usage
.sp
\fBtar\fR \fB\-A\fR [\fIOPTIONS\fR] \fIARCHIVE\fR \fIARCHIVE\fR
\fBtar\fR \fB\-A\fR [\fIOPTIONS\fR] \fB\-f\fR \fIARCHIVE\fR \fIARCHIVE\fR...
.sp
\fBtar\fR \fB\-c\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-d\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-t\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.sp
\fBtar\fR \fB\-r\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-t\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.sp
\fBtar\fR \fB\-u\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-x\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.SS GNU-style usage
.sp
\fBtar\fR {\fB\-\-catenate\fR|\fB\-\-concatenate\fR} [\fIOPTIONS\fR] \fIARCHIVE\fR \fIARCHIVE\fR
\fBtar\fR {\fB\-\-catenate\fR|\fB\-\-concatenate\fR} [\fIOPTIONS\fR] \fB\-\-file\fR \fIARCHIVE\fR \fIARCHIVE\fR...
.sp
\fBtar\fR \fB\-\-create\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
@@ -45,17 +42,15 @@ tar \- an archiving utility
.sp
\fBtar\fR \fB\-\-delete\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.sp
\fBtar\fR \fB\-\-append\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
\fBtar\fR \fB\-\-append\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-\-list\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
\fBtar\fR \fB\-\-list\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.sp
\fBtar\fR \fB\-\-test\-label\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fILABEL\fR...]
.sp
\fBtar\fR \fB\-\-update\fR [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR \fB\-\-update\fR [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIFILE\fR...]
.sp
\fBtar\fR {\fB\-\-extract\fR|\fB\-\-get\fR} [\fB\-f\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
\fBtar\fR {\fB\-\-extract\fR|\fB\-\-get\fR} [\fB\-\-file\fR \fIARCHIVE\fR] [\fIOPTIONS\fR] [\fIMEMBER\fR...]
.SH NOTE
This manpage is a short description of GNU \fBtar\fR. For a detailed
discussion, including examples and usage recommendations, refer to the
@@ -74,7 +69,7 @@ You can also view the manual using the info mode in
or find it in various formats online at
.PP
.RS +4
.B http://www.gnu.org/software/tar/manual
.B https://www.gnu.org/software/tar/manual
.RE
.PP
If any discrepancies occur between this manpage and the
@@ -85,11 +80,9 @@ GNU
.B tar
is an archiving program designed to store multiple files in a single
file (an \fBarchive\fR), and to manipulate such archives. The archive
can be either a regular file or a device (e.g. a tape drive, hence the name
can be either a regular file or a device (e.g., a tape drive, hence the name
of the program, which stands for \fBt\fRape \fBar\fRchiver), which can
be located either on the local or on a remote machine.
.PP
.SS Option styles
Options to GNU \fBtar\fR can be given in three different styles.
In
@@ -97,8 +90,8 @@ In
the first argument is a cluster of option letters and all subsequent
arguments supply arguments to those options that require them. The
arguments are read in the same order as the option letters. Any
command line words that remain after all options has been processed
are treated as non-optional arguments: file or archive member names.
command line words that remain after all options have been processed
are treated as non-option arguments: file or archive member names.
.PP
For example, the \fBc\fR option requires creating the archive, the
\fBv\fR option requests the verbose operation, and the \fBf\fR option
@@ -107,35 +100,37 @@ The following command, written in the traditional style, instructs tar
to store all files from the directory
.B /etc
into the archive file
.B etc.tar
.BR etc.tar ,
verbosely listing the files being archived:
.PP
.EX
.B tar cfv etc.tar /etc
tar cfv etc.tar /etc
.EE
.PP
In
.BR "UNIX " or " short-option style" ,
each option letter is prefixed with a single dash, as in other command
line utilities. If an option takes argument, the argument follows it,
line utilities. If an option takes an argument, the argument follows it,
either as a separate command line word, or immediately following the
option. However, if the option takes an \fBoptional\fR argument, the
argument must follow the option letter without any intervening
whitespace, as in \fB\-g/tmp/snar.db\fR.
.PP
Any number of options not taking arguments can be
clustered together after a single dash, e.g. \fB\-vkp\fR. Options
that take arguments (whether mandatory or optional), can appear at
the end of such a cluster, e.g. \fB\-vkpf a.tar\fR.
clustered together after a single dash, e.g.\& \fB\-vkp\fR. An option
that takes an argument (whether mandatory or optional) can appear at
the end of such a cluster, e.g.\& \fB\-vkpf a.tar\fR.
.PP
The example command above written in the
.B short-option style
could look like:
.PP
.EX
.B tar -cvf etc.tar /etc
tar \-cvf etc.tar /etc
.EE
or
.B tar -c -v -f etc.tar /etc
.EX
tar \-c \-v \-f etc.tar /etc
.EE
.PP
In
@@ -152,11 +147,11 @@ method.
Here are several ways of writing the example command in this style:
.PP
.EX
.B tar --create --file etc.tar --verbose /etc
tar \-\-create \-\-file etc.tar \-\-verbose /etc
.EE
or (abbreviating some options):
.EX
.B tar --cre --file=etc.tar --verb /etc
tar \-\-cre \-\-file=etc.tar \-\-verb /etc
.EE
.PP
The options in all three styles can be intermixed, although doing so
@@ -164,17 +159,17 @@ with old options is not encouraged.
.SS Operation mode
The options listed in the table below tell GNU \fBtar\fR what
operation it is to perform. Exactly one of them must be given.
Meaning of non-optional arguments depends on the operation mode
The meaning of non-option arguments depends on the operation mode
requested.
.TP
\fB\-A\fR, \fB\-\-catenate\fR, \fB\-\-concatenate\fR
Append archive to the end of another archive. The arguments are
Append archives to the end of another archive. The arguments are
treated as the names of archives to append. All archives must be of
the same format as the archive they are appended to, otherwise the
resulting archive might be unusable with non-GNU implementations of
\fBtar\fR. Notice also that when more than one archive is given, the
members from archives other than the first one will be accessible in
the resulting archive only if using the \fB\-i\fR
the resulting archive only when using the \fB\-i\fR
(\fB\-\-ignore\-zeros\fR) option.
Compressed archives cannot be concatenated.
@@ -218,7 +213,7 @@ There is no short option equivalent for this option.
.TP
\fB\-u\fR, \fB\-\-update\fR
Append files which are newer than the corresponding copy in the
archive. Arguments have the same meaning as with \fB\-c\fR and
archive. Arguments have the same meaning as with the \fB\-c\fR and
\fB\-r\fR options. Notice, that newer files don't replace their
old archive copies, but instead are appended to the end of archive.
The resulting archive can thus contain several members of the
@@ -228,16 +223,14 @@ same name, corresponding to various versions of the same file.
Extract files from an archive. Arguments are optional. When given,
they specify names of the archive members to be extracted.
.TP
.TP
\fB\-\-show\-defaults\fR
Show built-in defaults for various \fBtar\fR options and exit. No
arguments are allowed.
Show built-in defaults for various \fBtar\fR options and exit.
.TP
\fB\-?\fR, \fB\-\-help
Display a short option summary and exit. No arguments allowed.
Display a short option summary and exit.
.TP
\fB\-\-usage\fR
Display a list of available options and exit. No arguments allowed.
Display a list of available options and exit.
.TP
\fB\-\-version\fR
Print program version and copyright information and exit.
@@ -249,16 +242,15 @@ Check device numbers when creating incremental archives (default).
.TP
\fB\-g\fR, \fB\-\-listed\-incremental\fR=\fIFILE\fR
Handle new GNU-format incremental backups. \fIFILE\fR is the name of
a \fBsnapshot file\fR, where tar stores additional information which
a \fBsnapshot file\fR, where \fBtar\fR stores additional information which
is used to decide which files changed since the previous incremental
dump and, consequently, must be dumped again. If \fIFILE\fR does not
exist when creating an archive, it will be created and all files will
be added to the resulting archive (the \fBlevel 0\fR dump). To create
incremental archives of non-zero level \fBN\fR, create a copy of the
snapshot file created during the level \fBN-1\fR, and use it as
\fIFILE\fR.
incremental archives of non-zero level \fBN\fR, you need a copy of the
snapshot file created for level \fBN-1\fR, and use it as \fIFILE\fR.
When listing or extracting, the actual contents of \fIFILE\fR is not
When listing or extracting, the actual content of \fIFILE\fR is not
inspected, it is needed only due to syntactical requirements. It is
therefore common practice to use \fB/dev/null\fR in its place.
.TP
@@ -275,7 +267,7 @@ Handle old GNU-format incremental backups.
Do not exit with nonzero on unreadable files.
.TP
\fB\-\-level\fR=\fINUMBER\fR
Set dump level for created listed-incremental archive. Currently only
Set dump level for a created listed-incremental archive. Currently only
\fB\-\-level=0\fR is meaningful: it instructs \fBtar\fR to truncate
the snapshot file before dumping, thereby forcing a level 0 dump.
.TP
@@ -283,7 +275,7 @@ the snapshot file before dumping, thereby forcing a level 0 dump.
Assume the archive is seekable. Normally \fBtar\fR determines
automatically whether the archive can be seeked or not. This option
is intended for use in cases when such recognition fails. It takes
effect only if the archive is open for reading (e.g. with
effect only if the archive is open for reading (e.g., with
.B \-\-list
or
.B \-\-extract
@@ -307,7 +299,7 @@ either on the command line or via the \fB\-T\fR option. The default
Disable the use of some potentially harmful options.
.TP
\fB\-\-sparse\-version\fR=\fIMAJOR\fR[.\fIMINOR\fR]
Set version of the sparse format to use (implies \fB\-\-sparse\fR).
Set which version of the sparse format to use.
This option implies
.BR \-\-sparse .
Valid argument values are
@@ -315,7 +307,7 @@ Valid argument values are
.BR 0.1 ", and"
.BR 1.0 .
For a detailed discussion of sparse formats, refer to the \fBGNU Tar
Manual\fR, appendix \fBD\fR, "\fBSparse Formats\fR". Using \fBinfo\fR
Manual\fR, appendix \fBD\fR, "\fBSparse Formats\fR". Using the \fBinfo\fR
reader, it can be accessed running the following command:
.BR "info tar 'Sparse Formats'" .
.TP
@@ -370,7 +362,6 @@ Verify the archive after writing it.
.SS Output stream selection
.TP
\fB\-\-ignore\-command\-error\fR
.TP
Ignore subprocess exit codes.
.TP
\fB\-\-no\-ignore\-command\-error\fR
@@ -450,7 +441,7 @@ GNU \fBtar\fR version number.
The name of the archive \fBtar\fR is processing.
.TP
.B TAR_BLOCKING_FACTOR
Current blocking factor, i.e. number of 512-byte blocks in a record.
Current blocking factor, i.e., number of 512-byte blocks in a record.
.TP
.B TAR_VOLUME
Ordinal number of the volume \fBtar\fR is processing (set if
@@ -463,6 +454,7 @@ Format of the archive being processed. One of:
.BR posix ,
.BR ustar ,
.BR v7 .
.TP
.B TAR_SUBCOMMAND
A short option (with a leading dash) describing the operation \fBtar\fR is
executing.
@@ -472,7 +464,7 @@ executing.
\fB\-\-atime\-preserve\fR[=\fIMETHOD\fR]
Preserve access times on dumped files, either by restoring the times
after reading (\fIMETHOD\fR=\fBreplace\fR, this is the default) or by
not setting the times in the first place (\fIMETHOD\fR=\fBsystem\fR)
not setting the times in the first place (\fIMETHOD\fR=\fBsystem\fR).
.TP
\fB\-\-delay\-directory\-restore\fR
Delay setting modification times and permissions of extracted
@@ -560,14 +552,16 @@ As a result, each input file owned by \fIOLDUSR\fR will be
stored in archive with owner name \fINEWUSR\fR and UID \fINEWUID\fR.
.TP
\fB\-p\fR, \fB\-\-preserve\-permissions\fR, \fB\-\-same\-permissions\fR
extract information about file permissions (default for superuser)
Set permissions of extracted files to those recorded in the archive
(default for superuser).
.TP
\fB\-\-same\-owner\fR
Try extracting files with the same ownership as exists in the archive
(default for superuser).
.TP
\fB\-s\fR, \fB\-\-preserve\-order\fR, \fB\-\-same\-order\fR
Sort names to extract to match archive
Tell \fBtar\fR that the list of file names to process is sorted in the
same order as the files in the archive.
.TP
\fB\-\-sort=\fIORDER\fR
When creating an archive, sort directory entries according to
@@ -597,7 +591,7 @@ Disable POSIX ACLs support.
.B \-\-selinux
Enable SELinux context support.
.TP
.B \-\-no-selinux
.B \-\-no\-selinux
Disable SELinux context support.
.TP
.B \-\-xattrs
@@ -607,13 +601,13 @@ Enable extended attributes support.
Disable extended attributes support.
.TP
.BI \-\-xattrs\-exclude= PATTERN
Specify the exclude pattern for xattr keys. \fIPATTERN\fR is a POSIX
regular expression, e.g. \fB\-\-xattrs\-exclude='^user\.'\fR, to exclude
Specify the exclude pattern for xattr keys. \fIPATTERN\fR is a globbing
pattern, e.g.\& \fB\-\-xattrs\-exclude='user.*'\fR to include only
attributes from the user namespace.
.TP
.BI \-\-xattrs\-include= PATTERN
Specify the include pattern for xattr keys. \fIPATTERN\fR is a POSIX
regular expression.
Specify the include pattern for xattr keys. \fIPATTERN\fR is a globbing
pattern.
.SS Device selection and switching
.TP
\fB\-f\fR, \fB\-\-file\fR=\fIARCHIVE\fR
@@ -631,7 +625,7 @@ name or IP address, and the part after it as the file or device
pathname, e.g.:
.EX
--file=remotehost:/dev/sr0
\-\-file=remotehost:/dev/sr0
.EE
An optional username can be prefixed to the hostname, placing a \fB@\fR
@@ -644,7 +638,7 @@ command. Nowadays it is common to use
instead. You can do so by giving the following command line option:
.EX
--rsh-command=/usr/bin/ssh
\-\-rsh-command=/usr/bin/ssh
.EE
The remote machine should have the
@@ -670,7 +664,7 @@ GNU \fBtar\fR version number.
The name of the archive \fBtar\fR is processing.
.TP
.B TAR_BLOCKING_FACTOR
Current blocking factor, i.e. number of 512-byte blocks in a record.
Current blocking factor, i.e., number of 512-byte blocks in a record.
.TP
.B TAR_VOLUME
Ordinal number of the volume \fBtar\fR is processing (set if
@@ -741,14 +735,14 @@ end-of-file marker.
.TP
\fB\-i\fR, \fB\-\-ignore\-zeros\fR
Ignore zeroed blocks in archive. Normally two consecutive 512-blocks
filled with zeroes mean EOF and tar stops reading after encountering
filled with zeroes mean EOF and \fBtar\fR stops reading after encountering
them. This option instructs it to read further and is useful when
reading archives created with the \fB\-A\fR option.
.TP
\fB\-\-record\-size\fR=\fINUMBER\fR
Set record size. \fINUMBER\fR is the number of bytes per record. It
must be multiple of \fB512\fR. It can can be suffixed with a \fBsize
suffix\fR, e.g. \fB\-\-record-size=10K\fR, for 10 Kilobytes. See the
suffix\fR, e.g.\& \fB\-\-record-size=10K\fR, for 10 Kilobytes. See the
subsection
.BR "Size suffixes" ,
for a list of valid suffixes.
@@ -780,7 +774,8 @@ Same as \fB\-\-format=v7\fR.
\fB\-\-pax\-option\fR=\fIkeyword\fR[[:]=\fIvalue\fR][,\fIkeyword\fR[[:]=\fIvalue\fR]]...
Control pax keywords when creating \fBPAX\fR archives (\fB\-H
pax\fR). This option is equivalent to the \fB\-o\fR option of the
.BR pax (1) utility.
.BR pax (1)
utility.
.TP
\fB\-\-posix\fR
Same as \fB\-\-format=posix\fR.
@@ -853,17 +848,15 @@ Make numbered backups if numbered backups exist, simple backups otherwise.
.TP
.BR never ", " simple
Always make simple backups
.RS
.RE
.IP
If \fICONTROL\fR is not given, the value is taken from the
.B VERSION_CONTROL
environment variable. If it is not set, \fBexisting\fR is assumed.
.RE
.TP
\fB\-C\fR, \fB\-\-directory\fR=\fIDIR\fR
Change to \fIDIR\fR before performing any operations. This option is
order-sensitive, i.e. it affects all options that follow.
order-sensitive, i.e., it affects all options that follow.
.TP
\fB\-\-exclude\fR=\fIPATTERN\fR
Exclude files matching \fIPATTERN\fR, a
@@ -875,7 +868,15 @@ Exclude backup and lock files.
.TP
\fB\-\-exclude\-caches\fR
Exclude contents of directories containing file \fBCACHEDIR.TAG\fR,
except for the tag file itself.
except for the tag file itself. The \fBCACHEDIR.TAG\fR file must be
a regular file whose content begins with the following 43 characters:
.IP
.RS
.EX
Signature: 8a477f597d28d172789f06886806bc55
.EE
.RE
.TP
\fB\-\-exclude\-caches\-all\fR
Exclude directories containing file \fBCACHEDIR.TAG\fR and the file itself.
@@ -960,7 +961,7 @@ See also \fB\-\-verbatim\-files\-from\fR.
.TP
\fB\-N\fR, \fB\-\-newer\fR=\fIDATE\fR, \fB\-\-after\-date\fR=\fIDATE\fR
Only store files newer than DATE. If \fIDATE\fR starts with \fB/\fR
or \fB.\fR it is taken to be a file name; the ctime of that file is
or \fB.\fR it is taken to be a file name; the mtime of that file is
used as the date.
.TP
\fB\-\-one\-file\-system\fR
@@ -980,7 +981,7 @@ unless overridden by environment variable \fBSIMPLE_BACKUP_SUFFIX\fR.
Get names to extract or create from \fIFILE\fR.
Unless specified otherwise, the \fIFILE\fR must contain a list of
names separated by ASCII \fBLF\fR (i.e. one name per line). The
names separated by ASCII \fBLF\fR (i.e., one name per line). The
names read are handled the same way as command line arguments. They
undergo quote removal and word splitting, and any string that starts
with a \fB\-\fR is handled as \fBtar\fR command line option.
@@ -1003,13 +1004,13 @@ Treat each line obtained from a file list as a file name, even if it
starts with a dash. File lists are supplied with the
\fB\-\-files\-from\fR (\fB\-T\fR) option. The default behavior is to
handle names supplied in file lists as if they were typed in the
command line, i.e. any names starting with a dash are treated as
command line, i.e., any names starting with a dash are treated as
\fBtar\fR options. The \fB\-\-verbatim\-files\-from\fR option
disables this behavior.
This option affects all \fB\-\-files\-from\fR options that occur after
it in the command line. Its effect is reverted by the
\fB\-\-no\-verbatim\-files\-from} option.
\fB\-\-no\-verbatim\-files\-from\fR option.
This option is implied by the \fB\-\-null\fR option.
@@ -1059,7 +1060,8 @@ Display progress messages every \fIN\fRth record (default 10).
Run \fIACTION\fR on each checkpoint.
.TP
\fB\-\-clamp\-mtime\fR
Only set time when the file is more recent than what was given with \-\-mtime.
Only set time when the file is more recent than what was given with
\fB\-\-mtime\fR.
.TP
\fB\-\-full\-time\fR
Print file time to its full resolution.
@@ -1118,14 +1120,14 @@ Verbosely list files processed. Each instance of this option on the
command line increases the verbosity level by one. The maximum
verbosity level is 3. For a detailed discussion of how various
verbosity levels affect tar's output, please refer to \fBGNU Tar
Manual\fR, subsection 2.5.1 "\fBThe \-\-verbose Option\fR".
Manual\fR, subsection 2.5.2 "\fBThe '\-\-verbose' Option\fR".
.TP
\fB\-\-warning\fR=\fIKEYWORD\fR
Enable or disable warning messages identified by \fIKEYWORD\fR. The
messages are suppressed if \fIKEYWORD\fR is prefixed with \fBno\-\fR
and enabled otherwise.
Multiple \fB\-\-warning\fR messages accumulate.
Multiple \fB\-\-warning\fR options accumulate.
Keywords controlling general \fBtar\fR operation:
.RS
@@ -1142,7 +1144,7 @@ Disable all warning messages.
.B alone-zero-block
"A lone zero block at %s"
.HP
Keywords applicable for \fBtar --create\fR:
Keywords applicable for \fBtar \-\-create\fR:
.TP
.B cachedir
"%s: contains a cache directory tag %s; %s"
@@ -1164,7 +1166,7 @@ Keywords applicable for \fBtar --create\fR:
"%s: file is unchanged; not dumped"
.TP
.B ignore-archive
"%s: file is the archive; not dumped"
"%s: archive cannot contain itself; not dumped"
.TP
.B file-removed
"%s: File removed before we read it"
@@ -1178,7 +1180,7 @@ keyword applies only if used together with the
.B \-\-ignore\-failed\-read
option.
.HP
Keywords applicable for \fBtar --extract\fR:
Keywords applicable for \fBtar \-\-extract\fR:
.TP
.B existing\-file
"%s: skipping existing file"
@@ -1210,7 +1212,7 @@ default (unless \fB\-\-verbose\fR is used). A common example of what
you can get when using this warning is:
.EX
$ tar --warning=decompress-program -x -f archive.Z
$ tar \-\-warning=decompress-program \-x \-f archive.Z
tar (child): cannot run compress: No such file or directory
tar (child): trying gzip
.EE
@@ -1247,7 +1249,6 @@ Ask for confirmation for every action.
When creating, same as \fB\-\-old\-archive\fR. When extracting, same
as \fB\-\-no\-same\-owner\fR.
.SS Size suffixes
.sp
.nf
.ta 8n 18n 42n
.ul
@@ -1263,9 +1264,8 @@ as \fB\-\-no\-same\-owner\fR.
T Terabytes \fISIZE\fR x 1024^4
w Words \fISIZE\fR x 2
.fi
.PP
.SH "RETURN VALUE"
Tar exit code indicates whether it was able to successfully perform
Tar's exit code indicates whether it was able to successfully perform
the requested operation, and if not, what kind of error occurred.
.TP
.B 0
@@ -1273,12 +1273,13 @@ Successful termination.
.TP
.B 1
.I Some files differ.
If tar was invoked with the \fB\-\-compare\fR (\fB\-\-diff\fR, \fB\-d\fR)
If \fBtar\fR was invoked with the \fB\-\-compare\fR (\fB\-\-diff\fR, \fB\-d\fR)
command line option, this means that some files in the archive differ
from their disk counterparts. If tar was given one of the \fB\-\-create\fR,
\fB\-\-append\fR or \fB\-\-update\fR options, this exit code means
that some files were changed while being archived and so the resulting
archive does not contain the exact copy of the file set.
from their disk counterparts. If \fBtar\fR was given one of the
\fB\-\-create\fR, \fB\-\-append\fR or \fB\-\-update\fR options, this
exit code means that some files were changed while being archived and
so the resulting archive does not contain the exact copy of the file
set.
.TP
.B 2
.I Fatal error.
@@ -1289,7 +1290,7 @@ If a subprocess that had been invoked by
exited with a nonzero exit code,
.B tar
itself exits with that code as well. This can happen, for example, if
a compression option (e.g. \fB\-z\fR) was used and the external
a compression option (e.g.\& \fB\-z\fR) was used and the external
compressor program failed. Another example is
.B rmt
failure during backup to a remote device.
@@ -1314,11 +1315,11 @@ Online copies of \fBGNU tar\fR documentation in various formats can be
found at:
.PP
.in +4
.B http://www.gnu.org/software/tar/manual
.B https://www.gnu.org/software/tar/manual
.SH "BUG REPORTS"
Report bugs to <bug\-tar@gnu.org>.
.SH COPYRIGHT
Copyright \(co 2013 Free Software Foundation, Inc.
Copyright \(co 2013\(en2025 Free Software Foundation, Inc.
.br
.na
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

File diff suppressed because it is too large Load Diff

View File

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

View File

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

2
gnulib

Submodule gnulib updated: 4652c7bafa...4619f63e6f

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-2019 Free Software Foundation, Inc.
# Copyright 2005-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -18,15 +18,23 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
alloca
areadlinkat-with-size
argmatch
argp
argp-version-etc
assert-h
attribute
backupfile
bool
c-ctype
c32rtomb
c32tolower
c32toupper
closeout
configmake
dirname
dup2
errno-h
error
exclude
extern-inline
@@ -39,8 +47,10 @@ fdopendir
fdutimensat
file-has-acl
fileblocks
flexmember
fnmatch-gnu
fprintftime
free-posix
fseeko
fstatat
full-write
@@ -48,17 +58,24 @@ futimens
getline
getopt-gnu
getpagesize
gettext
gettext-h
gettime
gitlog-to-changelog
hash
human
ialloc
idx
intprops
inttostr
inttypes
inttypes-h
largefile
lchown
limits-h
linkat
localcharset
manywarnings
mbrtoc32-regular
mcel-prefer
mkdirat
mkdtemp
mkfifoat
@@ -71,26 +88,27 @@ progname
quote
quotearg
readlinkat
reallocarray
renameat
root-uid
rpmatch
full-read
safe-read
same-inode
savedir
selinux-at
setenv
snprintf
stat-time
stdbool
stdint
std-gnu23
stddef-h
stdint-h
stpcpy
stdopen
strdup-posix
strerror
strnlen
strtoimax
strtol
strtoul
strtoumax
symlinkat
sys_stat-h
timespec
timespec-sub
unlinkat
@@ -98,8 +116,11 @@ unlinkdir
unlocked-io
utimensat
version-etc-fsf
verror
xalignalloc
xalloc
xalloc-die
xgetcwd
xstrtoumax
xvasprintf
year2038-recommended

1
lib/.gitignore vendored
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-2019 Free Software Foundation, Inc.
# Copyright 1994-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -32,9 +32,7 @@ AM_CFLAGS = $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
noinst_HEADERS = \
paxlib.h\
rmt.h\
stdopen.h\
system.h\
system-ioctl.h\
wordsplit.h\
xattr-at.h
@@ -42,8 +40,7 @@ libtar_a_SOURCES = \
paxerror.c paxexit-status.c paxlib.h paxnames.c \
rtapelib.c \
rmt.h \
stdopen.c stdopen.h \
system.h system-ioctl.h \
system.h \
wordsplit.c\
xattr-at.c

View File

@@ -1,5 +1,5 @@
/* Replacement <attr/xattr.h> for platforms that lack it.
Copyright 2012-2019 Free Software Foundation, Inc.
Copyright 2012-2025 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,39 +22,39 @@
#endif
/* setting */
static inline int setxattr (const char *path, const char *name, const void
*value, size_t size, int flags)
static int setxattr (const char *path, const char *name, const void
*value, size_t size, int flags)
{ errno = ENOTSUP; return -1; }
static inline int lsetxattr (const char *path, const char *name, const void
*value, size_t size, int flags)
static int lsetxattr (const char *path, const char *name, const void
*value, size_t size, int flags)
{ errno = ENOTSUP; return -1; }
static inline int fsetxattr (int filedes, const char *name, const void *value,
size_t size, int flags)
static int fsetxattr (int filedes, const char *name, const void *value,
size_t size, int flags)
{ errno = ENOTSUP; return -1; }
/* getting */
static inline ssize_t getxattr (const char *path, const char *name, void *value,
size_t size)
static ssize_t getxattr (const char *path, const char *name, void *value,
size_t size)
{ errno = ENOTSUP; return -1; }
static inline ssize_t lgetxattr (const char *path, const char *name, void
*value, size_t size)
static ssize_t lgetxattr (const char *path, const char *name, void
*value, size_t size)
{ errno = ENOTSUP; return -1; }
static inline ssize_t fgetxattr (int filedes, const char *name, void *value,
size_t size)
static ssize_t fgetxattr (int filedes, const char *name, void *value,
size_t size)
{ errno = ENOTSUP; return -1; }
/* listing */
static inline ssize_t listxattr (const char *path, char *list, size_t size)
static ssize_t listxattr (const char *path, char *list, size_t size)
{ errno = ENOTSUP; return -1; }
static inline ssize_t llistxattr (const char *path, char *list, size_t size)
static ssize_t llistxattr (const char *path, char *list, size_t size)
{ errno = ENOTSUP; return -1; }
static inline ssize_t flistxattr (int filedes, char *list, size_t size)
static ssize_t flistxattr (int filedes, char *list, size_t size)
{ errno = ENOTSUP; return -1; }
#endif

View File

@@ -1,76 +0,0 @@
/* stdopen.c - ensure that the three standard file descriptors are in use
Copyright 2005-2019 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert and Jim Meyering. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "stdopen.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
/* Try to ensure that all of the standard file numbers (0, 1, 2)
are in use. Without this, each application would have to guard
every call to open, dup, fopen, etc. with tests to ensure they
don't use one of the special file numbers when opening a file.
Return false if at least one of the file descriptors is initially
closed and an attempt to reopen it fails. Otherwise, return true. */
bool
stdopen (void)
{
int fd;
bool ok = true;
for (fd = 0; fd <= 2; fd++)
{
if (fcntl (fd, F_GETFD) < 0)
{
if (errno != EBADF)
ok = false;
else
{
static const int contrary_mode[]
= { O_WRONLY, O_RDONLY, O_RDONLY };
int mode = contrary_mode[fd];
int new_fd;
/* Open /dev/null with the contrary mode so that the typical
read (stdin) or write (stdout, stderr) operation will fail.
With descriptor 0, we can do even better on systems that
have /dev/full, by opening that write-only instead of
/dev/null. The only drawback is that a write-provoked
failure comes with a misleading errno value, ENOSPC. */
if (mode == O_RDONLY
|| (new_fd = open ("/dev/full", mode) != fd))
new_fd = open ("/dev/null", mode);
if (new_fd != fd)
{
if (0 <= new_fd)
close (new_fd);
ok = false;
}
}
}
}
return ok;
}

View File

@@ -1,16 +0,0 @@
#ifndef STDOPEN_H
# define STDOPEN_H 1
# include <stdbool.h>
# ifdef __cplusplus
extern "C" {
# endif
bool stdopen (void);
# ifdef __cplusplus
}
# endif
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
/* Prototypes for openat-style fd-relative functions for operating with
extended file attributes.
Copyright 2012-2019 Free Software Foundation, Inc.
Copyright 2012-2025 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
# include <attr/xattr.h>
#endif
#include <errno.h>
#ifndef ENOATTR
# define ENOATTR ENODATA /* No such attribute */
#endif
@@ -50,7 +51,7 @@ int lsetxattrat (int dir_fd, const char *path, const char *name,
const void *value, size_t size, int flags);
/* dir-fd-relative getxattr. Operation gets the VALUE of the extended
attribute idenfified by NAME and associated with the given PATH in the
attribute identified by NAME and associated with the given PATH in the
filesystem relatively to directory identified by DIR_FD. For more info
about all parameters see the getxattr(2) manpage. */
ssize_t getxattrat (int dir_fd, const char *path, const char *name,
@@ -62,7 +63,7 @@ ssize_t getxattrat (int dir_fd, const char *path, const char *name,
ssize_t lgetxattrat (int dir_fd, const char *path, const char *name,
void *value, size_t size);
/* dir-fd-relative listxattr. Obtain the list of extended attrubtes names. For
/* dir-fd-relative listxattr. Obtain the list of extended attributes names. For
more info see the listxattr(2) manpage. */
ssize_t listxattrat (int dir_fd, const char *path, char *list, size_t size);

23
po/.gitignore vendored
View File

@@ -1,24 +1,23 @@
/Makevars.template~
/Makefile.in.in~
/Makefile.in.in
/Makevars.template
/Rules-quot
/boldquot.sed
/en@boldquot.header
/en@quot.header
/insert-header.sin
/quot.sed
/remove-potcdate.sed
/remove-potcdate.sin
*.gmo
*.mo
*.po
*~
.reference
LINGUAS
Makefile
Makefile.in
Makefile.in.in
Makevars
Makevars.template
POTFILES
Rules-quot
boldquot.sed
en@boldquot.header
en@quot.header
insert-header.sed
insert-header.sin
quot.sed
remove-potcdate.sed
remove-potcdate.sin
stamp-po
tar.pot

View File

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

View File

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

View File

@@ -11,12 +11,12 @@ TAR=/bin/tar
# (Optional) Path to rsh binary or its equivalent. You may wish to
# set it to ssh as shown in the example below, to improve security.
# In this case you will have to use public key authentication.
RSH=/usr/local/bin/ssh
RSH=/usr/bin/ssh
# (Optional) Path to rsh binary on remote mashines. This will be
# (Optional) Path to rsh binary on remote machines. This will be
# passed via --rsh-command option to the remote invocation of
# tar
RSH_COMMAND=/usr/local/bin/ssh
RSH_COMMAND=/usr/bin/ssh
# Name of temporary file to hold volume numbers. This needs to be accessible
# by all the machines which have filesystems to be dumped.
@@ -43,7 +43,7 @@ BACKUP_DIRS='remote1:/etc remote1:/var/spool/crontab'
# DIRLIST=/etc/my-backup/dirlist
# List of individual files to be dumped.
# These should be accesible from the machine on which the dump is run.
# These should be accessible from the machine on which the dump is run.
BACKUP_FILES=''
# This list may also be kept in file $SYSCONFDIR/backup/files, the
# format of which is the same as described above. The location of
@@ -82,7 +82,7 @@ SLEEP_MESSAGE="`awk '
}' /dev/null`"
# Copyright 2004-2019 Free Software Foundation, Inc.
# Copyright 2004-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@@ -1,7 +1,7 @@
#! /bin/sh
# Make backups.
# Copyright 2004-2006, 2013 Free Software Foundation
# Copyright 2004-2006, 2013, 2019 Free Software Foundation
# This file is part of GNU tar.
@@ -149,7 +149,7 @@ message 20 "Variables:"
message 20 "BACKUP_DIRS=$BACKUP_DIRS"
message 20 "BACKUP_FILES=$BACKUP_FILES"
# The buch of commands below is run in a subshell for which all output is
# The bunch of commands below is run in a subshell for which all output is
# piped through 'tee' to the logfile. Doing this, instead of having
# multiple pipelines all over the place, is cleaner and allows access to
# the exit value from various commands more easily.

View File

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

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-2019 Free Software Foundation, Inc.
# Copyright 2004-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.

View File

@@ -1,7 +1,7 @@
#! /bin/sh
# Restore backups.
# Copyright 2004-2019 Free Software Foundation, Inc.
# Copyright 2004-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.
@@ -183,7 +183,7 @@ restore_files()
done
}
# Operation Overwiew:
# Operation Overview:
#
# 1. Determine the time of the last backup
# 2. Create list of incremental listings to process

View File

@@ -1,6 +1,6 @@
#! /usr/bin/perl -w
# Display and edit the 'dev' field in tar's snapshots
# Copyright 2007-2019 Free Software Foundation, Inc.
# Copyright 2007-2025 Free Software Foundation, Inc.
# This file is part of GNU tar.

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* Checkpoint management for tar.
Copyright 2007-2019 Free Software Foundation, Inc.
Copyright 2007-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -19,10 +19,14 @@
#include <system.h>
#include "common.h"
#include "wordsplit.h"
#include <wordsplit.h>
#include <flexmember.h>
#include <fprintftime.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "fprintftime.h"
#include <signal.h>
enum checkpoint_opcode
@@ -47,103 +51,83 @@ struct checkpoint_action
char *command;
int signal;
} v;
char commandbuf[FLEXIBLE_ARRAY_MEMBER];
};
/* Checkpointing counter */
static unsigned checkpoint;
static intmax_t checkpoint;
/* List of checkpoint actions */
static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail;
static struct checkpoint_action *checkpoint_action,
**checkpoint_action_tail = &checkpoint_action;
/* State of the checkpoint system */
enum {
static enum {
CHKP_INIT, /* Needs initialization */
CHKP_COMPILE, /* Actions are being compiled */
CHKP_RUN /* Actions are being run */
};
static int checkpoint_state;
} checkpoint_state;
/* Blocked signals */
static sigset_t sigs;
static struct checkpoint_action *
alloc_action (enum checkpoint_opcode opcode)
alloc_action (enum checkpoint_opcode opcode, char const *quoted_string)
{
struct checkpoint_action *p = xzalloc (sizeof *p);
if (checkpoint_action_tail)
checkpoint_action_tail->next = p;
else
checkpoint_action = p;
checkpoint_action_tail = p;
idx_t quoted_size = quoted_string ? strlen (quoted_string) + 1 : 0;
struct checkpoint_action *p = xmalloc (FLEXSIZEOF (struct checkpoint_action,
commandbuf, quoted_size));
*checkpoint_action_tail = p;
checkpoint_action_tail = &p->next;
p->next = NULL;
p->opcode = opcode;
return p;
}
static char *
copy_string_unquote (const char *str)
{
char *output = xstrdup (str);
size_t len = strlen (output);
if ((*output == '"' || *output == '\'')
&& output[len-1] == *output)
if (quoted_string)
{
memmove (output, output+1, len-2);
output[len-2] = 0;
p->v.command = memcpy (p->commandbuf, quoted_string, quoted_size);
unquote_string (p->v.command);
}
unquote_string (output);
return output;
return p;
}
void
checkpoint_compile_action (const char *str)
{
struct checkpoint_action *act;
if (checkpoint_state == CHKP_INIT)
{
sigemptyset (&sigs);
checkpoint_state = CHKP_COMPILE;
}
if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
alloc_action (cop_dot);
alloc_action (cop_dot, NULL);
else if (strcmp (str, "bell") == 0)
alloc_action (cop_bell);
alloc_action (cop_bell, NULL);
else if (strcmp (str, "echo") == 0)
alloc_action (cop_echo);
alloc_action (cop_echo, NULL)->v.command = NULL;
else if (strncmp (str, "echo=", 5) == 0)
{
act = alloc_action (cop_echo);
act->v.command = copy_string_unquote (str + 5);
}
alloc_action (cop_echo, str + 5);
else if (strncmp (str, "exec=", 5) == 0)
{
act = alloc_action (cop_exec);
act->v.command = copy_string_unquote (str + 5);
}
alloc_action (cop_exec, str + 5);
else if (strncmp (str, "ttyout=", 7) == 0)
{
act = alloc_action (cop_ttyout);
act->v.command = copy_string_unquote (str + 7);
}
alloc_action (cop_ttyout, str + 7);
else if (strncmp (str, "sleep=", 6) == 0)
{
char const *arg = str + 6;
char *p;
time_t n = strtoul (str+6, &p, 10);
if (*p)
FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str));
act = alloc_action (cop_sleep);
act->v.time = n;
alloc_action (cop_sleep, NULL)->v.time
= stoint (arg, &p, NULL, 0, TYPE_MAXIMUM (time_t));
if ((p == arg) | *p)
paxfatal (0, _("%s: not a valid timeout"), str);
}
else if (strcmp (str, "totals") == 0)
alloc_action (cop_totals);
alloc_action (cop_totals, NULL);
else if (strncmp (str, "wait=", 5) == 0)
{
act = alloc_action (cop_wait);
act->v.signal = decode_signal (str + 5);
sigaddset (&sigs, act->v.signal);
int sig = decode_signal (str + 5);
alloc_action (cop_wait, NULL)->v.signal = sig;
sigaddset (&sigs, sig);
}
else
FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
paxfatal (0, _("%s: unknown checkpoint action"), str);
}
void
@@ -169,13 +153,7 @@ checkpoint_finish_compile (void)
}
}
static const char *checkpoint_total_format[] = {
"R",
"W",
"D"
};
static long
static intmax_t
getwidth (FILE *fp)
{
char const *columns;
@@ -189,8 +167,9 @@ getwidth (FILE *fp)
columns = getenv ("COLUMNS");
if (columns)
{
long int col = strtol (columns, NULL, 10);
if (0 < col)
char *end;
intmax_t col = stoint (columns, &end, NULL, 0, INTMAX_MAX);
if (! (*end | !col))
return col;
}
@@ -198,24 +177,20 @@ getwidth (FILE *fp)
}
static char *
getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
getarg (char const *input, char const **endp, char **argbuf, idx_t *arglen)
{
if (input[0] == '{')
{
char *p = strchr (input + 1, '}');
if (p)
{
size_t n = p - input;
idx_t n = p - input;
if (n > *arglen)
{
*arglen = n;
*argbuf = xrealloc (*argbuf, *arglen);
}
*argbuf = xpalloc (*argbuf, arglen, n - *arglen, -1, 1);
n--;
memcpy (*argbuf, input + 1, n);
(*argbuf)[n] = 0;
*endp = p + 1;
return *argbuf;
(*argbuf)[n] = 0;
return memcpy (*argbuf, input + 1, n);
}
}
@@ -223,38 +198,32 @@ getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
return NULL;
}
static int tty_cleanup;
static bool tty_cleanup;
static const char *def_format =
"%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
static int
format_checkpoint_string (FILE *fp, size_t len,
static intmax_t
format_checkpoint_string (FILE *fp, intmax_t len,
const char *input, bool do_write,
unsigned cpn)
intmax_t cpn)
{
const char *opstr = do_write ? gettext ("write") : gettext ("read");
char uintbuf[UINTMAX_STRSIZE_BOUND];
char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
const char *ip;
static char *argbuf = NULL;
static size_t arglen = 0;
static idx_t arglen = 0;
char *arg = NULL;
if (!input)
{
if (do_write)
/* TRANSLATORS: This is a "checkpoint of write operation",
*not* "Writing a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de escritura",
*not* "Escribiendo un punto de comprobaci@'on" */
*not* "Writing a checkpoint". */
input = gettext ("Write checkpoint %u");
else
/* TRANSLATORS: This is a "checkpoint of read operation",
*not* "Reading a checkpoint".
E.g. in Spanish "Punto de comprobaci@'on de lectura",
*not* "Leyendo un punto de comprobaci@'on" */
*not* "Reading a checkpoint". */
input = gettext ("Read checkpoint %u");
}
@@ -269,44 +238,52 @@ format_checkpoint_string (FILE *fp, size_t len,
{
fputc ('%', fp);
fputc (*ip, fp);
len += 2;
len = add_printf (len, 2);
continue;
}
}
switch (*ip)
{
case 'c':
len += format_checkpoint_string (fp, len, def_format, do_write,
cpn);
len = add_printf (len,
format_checkpoint_string (fp, len, def_format,
do_write, cpn));
break;
case 'u':
fputs (cps, fp);
len += strlen (cps);
len = add_printf (len, fprintf (fp, "%jd", cpn));
break;
case 's':
fputs (opstr, fp);
len += strlen (opstr);
len = add_printf (len, strlen (opstr));
break;
case 'd':
len += fprintf (fp, "%.0f", compute_duration ());
len = add_printf (len,
fprintf (fp, "%.0f",
compute_duration_ns () / BILLION));
break;
case 'T':
{
const char **fmt = checkpoint_total_format, *fmtbuf[3];
static char const *const checkpoint_total_format[]
= { "R", "W", "D" };
char const *const *fmt = checkpoint_total_format, *fmtbuf[3];
struct wordsplit ws;
compute_duration ();
compute_duration_ns ();
if (arg)
{
ws.ws_delim = ",";
if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
WRDSF_QUOTE | WRDSF_DELIM))
ERROR ((0, 0, _("cannot split string '%s': %s"),
arg, wordsplit_strerror (&ws)));
if (wordsplit (arg, &ws,
(WRDSF_NOVAR | WRDSF_NOCMD
| WRDSF_QUOTE | WRDSF_DELIM))
!= WRDSE_OK)
paxerror (0, _("cannot split string '%s': %s"),
arg, wordsplit_strerror (&ws));
else if (3 < ws.ws_wordc)
paxerror (0, _("too many words in '%s'"), arg);
else
{
int i;
@@ -318,7 +295,7 @@ format_checkpoint_string (FILE *fp, size_t len,
fmt = fmtbuf;
}
}
len += format_total_stats (fp, fmt, ',', 0);
len = add_printf (len, format_total_stats (fp, fmt, ',', 0));
if (arg)
wordsplit_free (&ws);
}
@@ -326,28 +303,37 @@ format_checkpoint_string (FILE *fp, size_t len,
case 't':
{
struct timeval tv;
struct tm *tm;
struct timespec ts = current_timespec ();
const char *fmt = arg ? arg : "%c";
gettimeofday (&tv, NULL);
tm = localtime (&tv.tv_sec);
len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
struct tm *tm = localtime (&ts.tv_sec);
len = add_printf (len,
(tm ? fprintftime (fp, fmt, tm, 0, ts.tv_nsec)
: fprintf (fp, "????""-??""-?? ??:??:??")));
}
break;
case '*':
{
long w = arg ? strtol (arg, NULL, 10) : getwidth (fp);
for (; w > len; len++)
fputc (' ', fp);
}
if (0 <= len)
{
intmax_t w;
if (!arg)
w = getwidth (fp);
else
{
char *end;
w = stoint (arg, &end, NULL, 0, INTMAX_MAX);
if ((end == arg) | *end)
w = 80;
}
for (; w > len; len++)
fputc (' ', fp);
}
break;
default:
fputc ('%', fp);
fputc (*ip, fp);
len += 2;
len = add_printf (len, 2);
break;
}
arg = NULL;
@@ -358,10 +344,10 @@ format_checkpoint_string (FILE *fp, size_t len,
if (*ip == '\r')
{
len = 0;
tty_cleanup = 1;
tty_cleanup = true;
}
else
len++;
len = add_printf (len, 1);
}
}
fflush (fp);
@@ -422,7 +408,7 @@ run_checkpoint_actions (bool do_write)
break;
case cop_totals:
compute_duration ();
compute_duration_ns ();
print_total_stats ();
break;
@@ -447,7 +433,7 @@ checkpoint_flush_actions (void)
case cop_ttyout:
if (tty && tty_cleanup)
{
long w = getwidth (tty);
intmax_t w = getwidth (tty);
while (w--)
fputc (' ', tty);
fputc ('\r', tty);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* Diff files from a tar archive.
Copyright 1988-2019 Free Software Foundation, Inc.
Copyright 1988-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -20,15 +20,21 @@
Written by John Gilmore, on 1987-04-30. */
#include <system.h>
#include <system-ioctl.h>
#if HAVE_LINUX_FD_H
# include <linux/fd.h>
#endif
#if HAVE_SYS_MTIO_H
# include <sys/ioctl.h>
# include <sys/mtio.h>
#endif
#include "common.h"
#include <alignalloc.h>
#include <quotearg.h>
#include <rmt.h>
#include <same-inode.h>
#include <stdarg.h>
/* Nonzero if we are verifying at the moment. */
@@ -44,8 +50,7 @@ static char *diff_buffer;
void
diff_init (void)
{
void *ptr;
diff_buffer = page_aligned_alloc (&ptr, record_size);
diff_buffer = xalignalloc (getpagesize (), record_size);
if (listed_incremental_option)
read_directory_file ();
}
@@ -72,21 +77,20 @@ report_difference (struct tar_stat_info *st, const char *fmt, ...)
}
/* Take a buffer returned by read_and_process and do nothing with it. */
static int
process_noop (size_t size __attribute__ ((unused)),
char *data __attribute__ ((unused)))
static bool
process_noop (MAYBE_UNUSED idx_t size, MAYBE_UNUSED char *data)
{
return 1;
return true;
}
static int
process_rawdata (size_t bytes, char *buffer)
static bool
process_rawdata (idx_t bytes, char *buffer)
{
size_t status = blocking_read (diff_handle, diff_buffer, bytes);
idx_t status = blocking_read (diff_handle, diff_buffer, bytes);
if (status != bytes)
if (status < bytes)
{
if (status == SAFE_READ_ERROR)
if (errno)
{
read_error (current_stat_info.file_name);
report_difference (&current_stat_info, NULL);
@@ -94,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 (memcmp (buffer, diff_buffer, bytes) != 0)
{
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);
}
@@ -149,23 +148,18 @@ read_and_process (struct tar_stat_info *st, int (*processor) (size_t, char *))
/* Call either stat or lstat over STAT_DATA, depending on
--dereference (-h), for a file which should exist. Diagnose any
problem. Return nonzero for success, zero otherwise. */
static int
problem. Return true for success, false otherwise. */
static bool
get_stat_data (char const *file_name, struct stat *stat_data)
{
int status = deref_stat (file_name, stat_data);
if (status != 0)
if (deref_stat (file_name, stat_data) < 0)
{
if (errno == ENOENT)
stat_warn (file_name);
else
stat_error (file_name);
(errno == ENOENT ? stat_warn : stat_error) (file_name);
report_difference (&current_stat_info, NULL);
return 0;
return false;
}
return 1;
return true;
}
@@ -229,8 +223,6 @@ diff_file (void)
}
else
{
int status;
if (current_stat_info.is_sparse)
sparse_diff_file (diff_handle, &current_stat_info);
else
@@ -241,12 +233,11 @@ diff_file (void)
{
struct timespec atime = get_stat_atime (&stat_data);
if (set_file_atime (diff_handle, chdir_fd, file_name, atime)
!= 0)
< 0)
utime_error (file_name);
}
status = close (diff_handle);
if (status != 0)
if (close (diff_handle) < 0)
close_error (file_name);
}
}
@@ -261,19 +252,18 @@ diff_link (void)
if (get_stat_data (current_stat_info.file_name, &file_data)
&& get_stat_data (current_stat_info.link_name, &link_data)
&& !sys_compare_links (&file_data, &link_data))
&& !psame_inode (&file_data, &link_data))
report_difference (&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,
@@ -294,7 +284,6 @@ diff_symlink (void)
if (linkbuf != buf)
free (linkbuf);
}
#endif
static void
diff_special (void)
@@ -330,37 +319,42 @@ diff_special (void)
report_difference (&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;
}
@@ -376,7 +370,7 @@ diff_dumpdir (struct tar_stat_info *dir)
int fd = subfile_open (dir->parent, dir->orig_file_name, open_read_flags);
if (fd < 0)
diag = open_diag;
else if (fstat (fd, &dir->stat))
else if (fstat (fd, &dir->stat) < 0)
{
diag = stat_diag;
close (fd);
@@ -393,7 +387,7 @@ diff_dumpdir (struct tar_stat_info *dir)
if (dumpdir_buffer)
{
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer))
if (dumpdir_cmp (dir->dumpdir, dumpdir_buffer) != 0)
report_difference (dir, _("Contents differ"));
}
else
@@ -403,16 +397,13 @@ diff_dumpdir (struct tar_stat_info *dir)
static void
diff_multivol (void)
{
struct stat stat_data;
int fd, status;
off_t offset;
if (current_stat_info.had_trailing_slash)
{
diff_dir ();
return;
}
struct stat stat_data;
if (!get_stat_data (current_stat_info.file_name, &stat_data))
return;
@@ -423,10 +414,11 @@ diff_multivol (void)
return;
}
offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
off_t offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
off_t file_size;
if (offset < 0
|| INT_ADD_OVERFLOW (current_stat_info.stat.st_size, offset)
|| stat_data.st_size != current_stat_info.stat.st_size + offset)
|| ckd_add (&file_size, current_stat_info.stat.st_size, offset)
|| stat_data.st_size != file_size)
{
report_difference (&current_stat_info, _("Size differs"));
skip_member ();
@@ -434,7 +426,7 @@ diff_multivol (void)
}
fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
int fd = openat (chdir_fd, current_stat_info.file_name, open_read_flags);
if (fd < 0)
{
@@ -452,8 +444,7 @@ diff_multivol (void)
else
read_and_process (&current_stat_info, process_rawdata);
status = close (fd);
if (status != 0)
if (close (fd) < 0)
close_error (current_stat_info.file_name);
}
@@ -476,9 +467,9 @@ diff_archive (void)
switch (current_header->header.typeflag)
{
default:
ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
quotearg_colon (current_stat_info.file_name),
current_header->header.typeflag));
paxerror (0, _("%s: Unknown file type '%c', diffed as normal file"),
quotearg_colon (current_stat_info.file_name),
current_header->header.typeflag);
FALLTHROUGH;
case AREGTYPE:
case REGTYPE:
@@ -497,11 +488,9 @@ diff_archive (void)
diff_link ();
break;
#ifdef HAVE_READLINK
case SYMTYPE:
diff_symlink ();
break;
#endif
case CHRTYPE:
case BLKTYPE:
@@ -527,22 +516,20 @@ diff_archive (void)
void
verify_volume (void)
{
int may_fail = 0;
bool may_fail = false;
if (removed_prefixes_p ())
{
WARN((0, 0,
_("Archive contains file names with leading prefixes removed.")));
may_fail = 1;
paxwarn (0,
_("Archive contains file names with leading prefixes removed."));
may_fail = true;
}
if (transform_program_p ())
{
WARN((0, 0,
_("Archive contains transformed file names.")));
may_fail = 1;
paxwarn (0, _("Archive contains transformed file names."));
may_fail = true;
}
if (may_fail)
WARN((0, 0,
_("Verification may fail to locate original files.")));
paxwarn (0, _("Verification may fail to locate original files."));
clear_directory_table ();
@@ -566,31 +553,12 @@ verify_volume (void)
ioctl (archive, FDFLUSH);
#endif
#ifdef MTIOCTOP
{
struct mtop operation;
int status;
operation.mt_op = MTBSF;
operation.mt_count = 1;
if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
{
if (errno != EIO
|| (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
status < 0))
{
#endif
if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
{
/* Lseek failed. Try a different method. */
seek_warn (archive_name_array[0]);
return;
}
#ifdef MTIOCTOP
}
}
}
#endif
if (!mtioseek (true, -1) && rmtlseek (archive, 0, SEEK_SET) < 0)
{
/* Lseek failed. Try a different method. */
seek_warn (archive_name_array[0]);
return;
}
access_mode = ACCESS_READ;
now_verifying = 1;
@@ -604,7 +572,7 @@ verify_volume (void)
if (status == HEADER_FAILURE)
{
int counter = 0;
intmax_t counter = 0;
do
{
@@ -615,10 +583,11 @@ verify_volume (void)
}
while (status == HEADER_FAILURE);
ERROR ((0, 0,
ngettext ("VERIFY FAILURE: %d invalid header detected",
"VERIFY FAILURE: %d invalid headers detected",
counter), counter));
paxerror (0,
ngettext ("VERIFY FAILURE: %jd invalid header detected",
"VERIFY FAILURE: %jd invalid headers detected",
counter),
counter);
}
if (status == HEADER_END_OF_FILE)
break;
@@ -627,20 +596,18 @@ verify_volume (void)
set_next_block_after (current_header);
if (!ignore_zeros_option)
{
char buf[UINTMAX_STRSIZE_BOUND];
status = read_header (&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-2019 Free Software Foundation, Inc.
Copyright 1988-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -18,24 +18,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <system.h>
#include <system-ioctl.h>
#include "common.h"
#include <rmt.h>
static union block *new_record;
static int new_blocks;
static idx_t new_blocks;
static bool acting_as_filter;
/* FIXME: This module should not directly handle the following
variables, instead, the interface should be cleaned up. */
extern union block *record_start;
extern union block *record_end;
extern union block *current_block;
extern union block *recent_long_name;
extern union block *recent_long_link;
extern off_t records_read;
/* The number of records skipped at the start of the archive, when
passing over members that are not deleted. */
off_t records_skipped;
@@ -50,47 +40,37 @@ move_archive (off_t count)
if (count == 0)
return;
#ifdef MTIOCTOP
{
struct mtop operation;
if (count < 0
? (operation.mt_op = MTBSR,
operation.mt_count = -count,
operation.mt_count == -count)
: (operation.mt_op = MTFSR,
operation.mt_count = count,
operation.mt_count == count))
{
if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
return;
if (errno == EIO
&& 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
return;
}
}
#endif /* MTIOCTOP */
{
off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR);
off_t increment = record_size * (off_t) count;
off_t position = position0 + increment;
if (increment / count != record_size
|| (position < position0) != (increment < 0)
|| (position = position < 0 ? 0 : position,
rmtlseek (archive, position, SEEK_SET) != position))
seek_error_details (archive_name_array[0], position);
if (mtioseek (false, count))
return;
}
off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
if (0 <= position0)
{
/* Pretend the starting position is at the first record
boundary after POSITION0. This is useful at EOF after
a short read. */
idx_t short_size = position0 % record_size;
idx_t start_offset = short_size ? record_size - short_size : 0;
off_t increment, move_start;
if (ckd_mul (&increment, record_size, count)
|| ckd_add (&move_start, position0, start_offset)
|| ckd_add (&position, move_start, increment)
|| position < 0)
{
paxerror (EOVERFLOW, "lseek: %s", archive_name_array[0]);
return;
}
else if (rmtlseek (archive, position, SEEK_SET) == position)
return;
}
if (!_isrmt (archive))
seek_error_details (archive_name_array[0], position);
}
/* Write out the record which has been filled. If MOVE_BACK_FLAG,
backspace to where we started. */
static void
write_record (int move_back_flag)
write_record (bool move_back_flag)
{
union block *save_record = record_start;
record_start = new_record;
@@ -121,22 +101,21 @@ write_record (int move_back_flag)
}
static void
write_recent_blocks (union block *h, size_t blocks)
write_recent_blocks (union block *h, idx_t blocks)
{
size_t i;
for (i = 0; i < blocks; i++)
for (idx_t i = 0; i < blocks; i++)
{
new_record[new_blocks++] = h[i];
if (new_blocks == blocking_factor)
write_record (1);
write_record (true);
}
}
static void
write_recent_bytes (char *data, size_t bytes)
write_recent_bytes (char *data, idx_t bytes)
{
size_t blocks = bytes / BLOCKSIZE;
size_t rest = bytes - blocks * BLOCKSIZE;
idx_t blocks = bytes >> LG_BLOCKSIZE;
idx_t rest = bytes & (BLOCKSIZE - 1);
write_recent_blocks ((union block *)data, blocks);
memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
@@ -144,7 +123,25 @@ write_recent_bytes (char *data, size_t bytes)
memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
new_blocks++;
if (new_blocks == blocking_factor)
write_record (1);
write_record (true);
}
static void
flush_file (void)
{
set_next_block_after (current_header);
off_t size = current_stat_info.stat.st_size;
off_t blocks_to_skip = (size >> LG_BLOCKSIZE) + !!(size & (BLOCKSIZE - 1));
while (record_end - current_block <= blocks_to_skip)
{
blocks_to_skip -= (record_end - current_block);
flush_archive ();
if (record_end == current_block)
/* Hit EOF */
return;
}
current_block += blocks_to_skip;
}
void
@@ -155,19 +152,19 @@ delete_archive_members (void)
/* FIXME: Should clean the routine before cleaning these variables :-( */
struct name *name;
off_t blocks_to_skip = 0;
off_t blocks_to_keep = 0;
int kept_blocks_in_record;
ptrdiff_t kept_blocks_in_record;
name_gather ();
open_archive (ACCESS_UPDATE);
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
/* Skip to the first member that matches the name list. */
do
{
enum read_header status = read_header (&current_header,
&current_stat_info,
read_header_x_raw);
&current_stat_info,
read_header_x_raw);
switch (status)
{
@@ -175,15 +172,15 @@ delete_archive_members (void)
abort ();
case HEADER_SUCCESS:
if ((name = name_scan (current_stat_info.file_name)) == NULL)
if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
{
skip_member ();
skim_member (acting_as_filter);
break;
}
name->found_count++;
if (!ISFOUND(name))
if (!isfound (name))
{
skip_member ();
skim_member (acting_as_filter);
break;
}
FALLTHROUGH;
@@ -207,12 +204,12 @@ delete_archive_members (void)
switch (previous_status)
{
case HEADER_STILL_UNREAD:
WARN ((0, 0, _("This does not look like a tar archive")));
paxwarn (0, _("This does not look like a tar archive"));
FALLTHROUGH;
case HEADER_SUCCESS:
case HEADER_SUCCESS_EXTENDED:
case HEADER_ZERO_BLOCK:
ERROR ((0, 0, _("Skipping to next header")));
paxerror (0, _("Skipping to next header"));
FALLTHROUGH;
case HEADER_FAILURE:
break;
@@ -243,15 +240,12 @@ delete_archive_members (void)
if (logical_status == HEADER_SUCCESS)
{
/* FIXME: Pheew! This is crufty code! */
logical_status = HEADER_STILL_UNREAD;
goto flush_file;
flush_file ();
}
/* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
"delete.c", line 223: warning: loop not entered at top
Reported by Bruno Haible. */
while (1)
/* Skip matching members and move the rest up the archive. */
while (logical_status != HEADER_END_OF_FILE)
{
enum read_header status;
@@ -259,105 +253,109 @@ delete_archive_members (void)
if (current_block == record_end)
flush_archive ();
status = read_header (&current_header, &current_stat_info,
read_header_auto);
read_header_auto);
xheader_decode (&current_stat_info);
if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
switch (status)
{
case HEADER_STILL_UNREAD:
case HEADER_SUCCESS_EXTENDED:
abort ();
case HEADER_SUCCESS:
/* Found another header. */
xheader_decode (&current_stat_info);
if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
{
name->found_count++;
if (isfound (name))
{
flush_file ();
break;
}
}
/* Copy header. */
if (current_stat_info.xhdr.size)
{
write_recent_bytes (current_stat_info.xhdr.buffer,
current_stat_info.xhdr.size);
}
else
{
write_recent_blocks (recent_long_name,
recent_long_name_blocks);
write_recent_blocks (recent_long_link,
recent_long_link_blocks);
}
new_record[new_blocks] = *current_header;
new_blocks++;
blocks_to_keep
= ((current_stat_info.stat.st_size >> LG_BLOCKSIZE)
+ !!(current_stat_info.stat.st_size & (BLOCKSIZE - 1)));
set_next_block_after (current_header);
continue;
}
if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
{
if (new_blocks == blocking_factor)
write_record (true);
/* Copy data. */
kept_blocks_in_record = record_end - current_block;
if (kept_blocks_in_record > blocks_to_keep)
kept_blocks_in_record = blocks_to_keep;
while (blocks_to_keep)
{
ptrdiff_t count;
if (current_block == record_end)
{
flush_read ();
current_block = record_start;
kept_blocks_in_record = blocking_factor;
if (kept_blocks_in_record > blocks_to_keep)
kept_blocks_in_record = blocks_to_keep;
}
count = kept_blocks_in_record;
if (blocking_factor - new_blocks < count)
count = blocking_factor - new_blocks;
if (! count)
abort ();
memcpy (new_record + new_blocks, current_block,
count * BLOCKSIZE);
new_blocks += count;
current_block += count;
blocks_to_keep -= count;
kept_blocks_in_record -= count;
if (new_blocks == blocking_factor)
write_record (true);
}
break;
case HEADER_ZERO_BLOCK:
if (ignore_zeros_option)
set_next_block_after (current_header);
else
logical_status = HEADER_END_OF_FILE;
break;
case HEADER_END_OF_FILE:
logical_status = HEADER_END_OF_FILE;
break;
}
if (status == HEADER_FAILURE)
{
ERROR ((0, 0, _("Deleting non-header from archive")));
case HEADER_FAILURE:
paxerror (0, _("Deleting non-header from archive"));
set_next_block_after (current_header);
continue;
}
/* Found another header. */
if ((name = name_scan (current_stat_info.file_name)) != NULL)
{
name->found_count++;
if (ISFOUND(name))
{
flush_file:
set_next_block_after (current_header);
blocks_to_skip = (current_stat_info.stat.st_size
+ BLOCKSIZE - 1) / BLOCKSIZE;
while (record_end - current_block <= blocks_to_skip)
{
blocks_to_skip -= (record_end - current_block);
flush_archive ();
}
current_block += blocks_to_skip;
blocks_to_skip = 0;
continue;
}
}
/* Copy header. */
if (current_stat_info.xhdr.size)
{
write_recent_bytes (current_stat_info.xhdr.buffer,
current_stat_info.xhdr.size);
}
else
{
write_recent_blocks (recent_long_name, recent_long_name_blocks);
write_recent_blocks (recent_long_link, recent_long_link_blocks);
}
new_record[new_blocks] = *current_header;
new_blocks++;
blocks_to_keep
= (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
set_next_block_after (current_header);
if (new_blocks == blocking_factor)
write_record (1);
/* Copy data. */
kept_blocks_in_record = record_end - current_block;
if (kept_blocks_in_record > blocks_to_keep)
kept_blocks_in_record = blocks_to_keep;
while (blocks_to_keep)
{
int count;
if (current_block == record_end)
{
flush_read ();
current_block = record_start;
kept_blocks_in_record = blocking_factor;
if (kept_blocks_in_record > blocks_to_keep)
kept_blocks_in_record = blocks_to_keep;
}
count = kept_blocks_in_record;
if (blocking_factor - new_blocks < count)
count = blocking_factor - new_blocks;
if (! count)
abort ();
memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE);
new_blocks += count;
current_block += count;
blocks_to_keep -= count;
kept_blocks_in_record -= count;
if (new_blocks == blocking_factor)
write_record (1);
break;
default:
abort ();
}
tar_stat_destroy (&current_stat_info);
}
if (logical_status == HEADER_END_OF_FILE)
@@ -365,11 +363,11 @@ delete_archive_members (void)
/* Write the end of tape. FIXME: we can't use write_eot here,
as it gets confused when the input is at end of file. */
int total_zero_blocks = 0;
idx_t total_zero_blocks = 0;
do
{
int zero_blocks = blocking_factor - new_blocks;
idx_t zero_blocks = blocking_factor - new_blocks;
memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
total_zero_blocks += zero_blocks;
write_record (total_zero_blocks < 2);
@@ -379,7 +377,7 @@ delete_archive_members (void)
if (! acting_as_filter && ! _isrmt (archive))
{
if (sys_truncate (archive))
if (sys_truncate (archive) < 0)
truncate_warn (archive_name_array[0]);
}
}

View File

@@ -1,6 +1,6 @@
/* Per-directory exclusion files for tar.
Copyright 2014-2019 Free Software Foundation, Inc.
Copyright 2014-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -18,7 +18,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <system.h>
#include <c-ctype.h>
#include <quotearg.h>
#include <flexmember.h>
#include <fnmatch.h>
#include <wordsplit.h>
#include "common.h"
@@ -40,7 +42,7 @@ struct excfile
{
struct excfile *next;
int flags;
char name[1];
char name[FLEXIBLE_ARRAY_MEMBER];
};
static struct excfile *excfile_head, *excfile_tail;
@@ -48,7 +50,8 @@ static struct excfile *excfile_head, *excfile_tail;
void
excfile_add (const char *name, int flags)
{
struct excfile *p = xmalloc (sizeof (*p) + strlen (name));
struct excfile *p = xmalloc (FLEXNSIZEOF (struct excfile, name,
strlen (name) + 1));
p->next = NULL;
p->flags = flags;
strcpy (p->name, name);
@@ -69,53 +72,45 @@ struct exclist
void
info_attach_exclist (struct tar_stat_info *dir)
{
struct excfile *file;
struct exclist *head = NULL, *tail = NULL, *ent;
struct vcs_ignore_file *vcsfile;
struct exclist *head = NULL, *tail = NULL;
if (dir->exclude_list)
return;
for (file = excfile_head; file; file = file->next)
for (struct excfile *file = excfile_head; file; file = file->next)
{
if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
if (faccessat (dir->fd, file->name, F_OK, 0) == 0)
{
FILE *fp;
struct exclude *ex = NULL;
int fd = subfile_open (dir, file->name, O_RDONLY);
if (fd == -1)
if (fd < 0)
{
open_error (file->name);
continue;
}
fp = fdopen (fd, "r");
FILE *fp = fdopen (fd, "r");
if (!fp)
{
ERROR ((0, errno, _("%s: fdopen failed"), file->name));
paxerror (errno, _("%s: fdopen failed"), file->name);
close (fd);
continue;
}
if (!ex)
ex = new_exclude ();
struct exclude *ex = new_exclude ();
vcsfile = get_vcs_ignore_file (file->name);
struct vcs_ignore_file *vcsfile = get_vcs_ignore_file (file->name);
if (vcsfile->initfn)
vcsfile->data = vcsfile->initfn (vcsfile->data);
if (add_exclude_fp (vcsfile->addfn, ex, fp,
EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n',
FNM_FILE_NAME|EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED,
'\n',
vcsfile->data))
{
int e = errno;
FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
}
paxfatal (errno, "%s", quotearg_colon (file->name));
fclose (fp);
ent = xmalloc (sizeof (*ent));
struct exclist *ent = xmalloc (sizeof *ent);
ent->excluded = ex;
ent->flags = file->flags == EXCL_DEFAULT
? file->flags : vcsfile->flags;
ent->flags = file->flags;
ent->prev = tail;
ent->next = NULL;
@@ -197,23 +192,26 @@ excluded_name (char const *name, struct tar_stat_info *st)
}
static void
cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data)
cvs_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
struct wordsplit ws;
size_t i;
options |= EXCLUDE_ALLOC;
if (wordsplit (pattern, &ws,
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS))
WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)
!= WRDSE_OK)
return;
for (i = 0; i < ws.ws_wordc; i++)
for (idx_t i = 0; i < ws.ws_wordc; i++)
add_exclude (ex, ws.ws_wordv[i], options);
wordsplit_free (&ws);
}
static void
git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
git_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (isspace (*pattern))
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
@@ -223,9 +221,10 @@ git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
}
static void
bzr_addfn (struct exclude *ex, char const *pattern, int options, void *data)
bzr_addfn (struct exclude *ex, char const *pattern, int options,
MAYBE_UNUSED void *data)
{
while (isspace (*pattern))
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
@@ -260,15 +259,14 @@ static void
hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
{
int *hgopt = data;
size_t len;
while (isspace (*pattern))
while (c_isspace (*pattern))
++pattern;
if (*pattern == 0 || *pattern == '#')
return;
if (strncmp (pattern, "syntax:", 7) == 0)
{
for (pattern += 7; isspace (*pattern); ++pattern)
for (pattern += 7; c_isspace (*pattern); ++pattern)
;
if (strcmp (pattern, "regexp") == 0)
/* FIXME: Regexps must be perl-style */
@@ -279,7 +277,7 @@ hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
return;
}
len = strlen(pattern);
idx_t len = strlen (pattern);
if (pattern[len-1] == '/')
{
char *p;
@@ -325,5 +323,5 @@ exclude_vcs_ignores (void)
struct vcs_ignore_file *p;
for (p = vcs_ignore_files; p->filename; p++)
excfile_add (p->filename, EXCL_DEFAULT);
excfile_add (p->filename, p->flags);
}

View File

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

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

116
src/map.c
View File

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

View File

@@ -1,6 +1,6 @@
/* Miscellaneous functions, not really specific to GNU tar.
Copyright 1988-2019 Free Software Foundation, Inc.
Copyright 1988-2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -19,6 +19,7 @@
#include <system.h>
#include <rmt.h>
#include "common.h"
#include <c-ctype.h>
#include <quotearg.h>
#include <xgetcwd.h>
#include <unlinkdir.h>
@@ -30,7 +31,7 @@
static void namebuf_add_dir (namebuf_t, char const *);
static char *namebuf_finish (namebuf_t);
static const char *tar_getcdpath (int);
static const char *tar_getcdpath (idx_t);
char const *
quote_n_colon (int n, char const *arg)
@@ -43,24 +44,43 @@ quote_n_colon (int n, char const *arg)
/* Assign STRING to a copy of VALUE if not zero, or to zero. If
STRING was nonzero, it is freed first. */
void
assign_string (char **string, const char *value)
assign_string_or_null (char **string, const char *value)
{
free (*string);
*string = value ? xstrdup (value) : 0;
if (value)
assign_string (string, value);
else
assign_null (string);
}
void
assign_string_n (char **string, const char *value, size_t n)
assign_string (char **string, const char *value)
{
free (*string);
*string = xstrdup (value);
}
void
assign_null (char **string)
{
char *old = *string;
*string = NULL;
free (old);
}
void
assign_string_n (char **string, const char *value, idx_t n)
{
free (*string);
if (value)
{
size_t l = strnlen (value, n);
idx_t l = strnlen (value, n);
char *p = xmalloc (l + 1);
memcpy (p, value, l);
p[l] = 0;
*string = p;
}
else
*string = NULL;
}
#if 0
@@ -84,20 +104,20 @@ quote_copy_string (const char *string)
const char *source = string;
char *destination = 0;
char *buffer = 0;
int copying = 0;
bool copying = false;
while (*source)
{
int character = *source++;
char character = *source++;
switch (character)
{
case '\n': case '\\':
if (!copying)
{
size_t length = (source - string) - 1;
idx_t length = (source - string) - 1;
copying = 1;
copying = true;
buffer = xmalloc (length + 2 + 2 * strlen (source) + 1);
memcpy (buffer, string, length);
destination = buffer + length;
@@ -121,20 +141,18 @@ quote_copy_string (const char *string)
}
#endif
/* Takes a quoted C string (like those produced by quote_copy_string)
and turns it back into the un-quoted original. This is done in
place. Returns 0 only if the string was not properly quoted, but
completes the unquoting anyway.
/* Take a quoted C string (like those produced by quote_copy_string)
and turn it back into the un-quoted original, in place.
Complete the unquoting even if the string was not properly quoted.
This is used for reading the saved directory file in incremental
dumps. It is used for decoding old 'N' records (demangling names).
But also, it is used for decoding file arguments, would they come
from the shell or a -T file, and for decoding the --exclude
argument. */
int
void
unquote_string (char *string)
{
int result = 1;
char *source = string;
char *destination = string;
@@ -201,26 +219,24 @@ unquote_string (char *string)
case '6':
case '7':
{
int value = *source++ - '0';
unsigned char value = *source++ - '0';
if (*source < '0' || *source > '7')
{
*destination++ = value;
break;
}
value = value * 8 + *source++ - '0';
if (*source < '0' || *source > '7')
unsigned char val1 = value * 8 + (*source++ - '0'), val2;
if (*source < '0' || *source > '7' || ckd_mul (&val2, val1, 8))
{
*destination++ = value;
break;
}
value = value * 8 + *source++ - '0';
*destination++ = value;
*destination++ = val2 + (*source++ - '0');
break;
}
default:
result = 0;
*destination++ = '\\';
if (*source)
*destination++ = *source++;
@@ -233,7 +249,6 @@ unquote_string (char *string)
if (source != destination)
*destination = '\0';
return result;
}
/* Zap trailing slashes. */
@@ -295,7 +310,7 @@ normalize_filename_x (char *file_name)
Return a normalized newly-allocated copy. */
char *
normalize_filename (int cdidx, const char *name)
normalize_filename (idx_t cdidx, const char *name)
{
char *copy = NULL;
@@ -311,11 +326,9 @@ normalize_filename (int cdidx, const char *name)
should use dev+ino pairs instead of names? (See listed03.at for
a related test case.) */
const char *cdpath = tar_getcdpath (cdidx);
size_t copylen;
idx_t copylen;
bool need_separator;
if (!cdpath)
call_arg_fatal ("getcwd", ".");
copylen = strlen (cdpath);
need_separator = ! (DOUBLE_SLASH_IS_DISTINCT_ROOT
&& copylen == 2 && ISSLASH (cdpath[1]));
@@ -333,11 +346,11 @@ normalize_filename (int cdidx, const char *name)
void
replace_prefix (char **pname, const char *samp, size_t slen,
const char *repl, size_t rlen)
replace_prefix (char **pname, const char *samp, idx_t slen,
const char *repl, idx_t rlen)
{
char *name = *pname;
size_t nlen = strlen (name);
idx_t nlen = strlen (name);
if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
{
if (rlen > slen)
@@ -354,18 +367,17 @@ replace_prefix (char **pname, const char *samp, size_t slen,
/* Handling numbers. */
/* Convert VALUE, which is converted from a system integer type whose
minimum value is MINVAL and maximum MINVAL, to an decimal
minimum value is MINVAL and maximum MINVAL, to a decimal
integer string. Use the storage in BUF and return a pointer to the
converted string. If VALUE is converted from a negative integer in
the range MINVAL .. -1, represent it with a string representation
of the negative integer, using leading '-'. */
#if ! (INTMAX_MAX <= UINTMAX_MAX / 2)
# error "sysinttostr: uintmax_t cannot represent all intmax_t values"
#endif
char *
sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval,
char buf[SYSINT_BUFSIZE])
{
static_assert (INTMAX_MAX <= UINTMAX_MAX / 2);
if (value <= maxval)
return umaxtostr (value, buf);
else
@@ -375,52 +387,110 @@ sysinttostr (uintmax_t value, intmax_t minval, uintmax_t maxval,
}
}
/* Convert a prefix of the string ARG to a system integer type whose
minimum value is MINVAL and maximum MAXVAL. If MINVAL is negative,
/* Convert T to a decimal integer string. Use the storage in BUF and
return a pointer to the converted string. */
char *
timetostr (time_t t, char buf[SYSINT_BUFSIZE])
{
return sysinttostr (t, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), buf);
}
/* Convert a prefix of the string ARG to a system integer type.
If ARGLIM, set *ARGLIM to point to just after the prefix.
If OVERFLOW, set *OVERFLOW to true or false
depending on whether the input is out of MINVAL..MAXVAL range.
If the input is out of that range, return an extreme value.
MINVAL must not be positive.
If MINVAL is negative, MAXVAL can be at most INTMAX_MAX, and
negative integers MINVAL .. -1 are assumed to be represented using
leading '-' in the usual way. If the represented value exceeds
INTMAX_MAX, return a negative integer V such that (uintmax_t) V
yields the represented value. If ARGLIM is nonnull, store into
*ARGLIM a pointer to the first character after the prefix.
yields the represented value.
On conversion error: if ARGLIM set *ARGLIM = ARG; if OVERFLOW set
*OVERFLOW = false; then return 0.
This is the inverse of sysinttostr.
On a normal return, set errno = 0.
On conversion error, return 0 and set errno = EINVAL.
On overflow, return an extreme value and set errno = ERANGE. */
#if ! (INTMAX_MAX <= UINTMAX_MAX)
# error "strtosysint: nonnegative intmax_t does not fit in uintmax_t"
#endif
Sample call to this function:
char *s_end;
bool overflow;
idx_t i = stoint (s, &s_end, &overflow, 0, IDX_MAX);
if ((s_end == s) | *s_end | overflow)
diagnose_invalid (s);
This example uses "|" instead of "||" for fewer branches at runtime,
which tends to be more efficient on modern processors.
This function is named "stoint" instead of "strtoint" because
<string.h> reserves names beginning with "str". */
intmax_t
strtosysint (char const *arg, char **arglim, intmax_t minval, uintmax_t maxval)
stoint (char const *arg, char **arglim, bool *overflow,
intmax_t minval, uintmax_t maxval)
{
errno = 0;
if (maxval <= INTMAX_MAX)
static_assert (INTMAX_MAX <= UINTMAX_MAX);
char const *p = arg;
intmax_t i;
bool v = false;
if (c_isdigit (*p))
{
if (ISDIGIT (arg[*arg == '-']))
if (minval < 0)
{
intmax_t i = strtoimax (arg, arglim, 10);
intmax_t imaxval = maxval;
if (minval <= i && i <= imaxval)
return i;
errno = ERANGE;
return i < minval ? minval : maxval;
i = *p - '0';
while (c_isdigit (*++p))
{
v |= ckd_mul (&i, i, 10);
v |= ckd_add (&i, i, *p - '0');
}
v |= maxval < i;
if (v)
i = maxval;
}
else
{
uintmax_t u = *p - '0';
while (c_isdigit (*++p))
{
v |= ckd_mul (&u, u, 10);
v |= ckd_add (&u, u, *p - '0');
}
v |= maxval < u;
if (v)
u = maxval;
i = represent_uintmax (u);
}
}
else if (minval < 0 && *p == '-' && c_isdigit (p[1]))
{
p++;
i = - (*p - '0');
while (c_isdigit (*++p))
{
v |= ckd_mul (&i, i, 10);
v |= ckd_sub (&i, i, *p - '0');
}
v |= i < minval;
if (v)
i = minval;
}
else
{
if (ISDIGIT (*arg))
{
uintmax_t i = strtoumax (arg, arglim, 10);
if (i <= maxval)
return represent_uintmax (i);
errno = ERANGE;
return maxval;
}
}
i = 0;
errno = EINVAL;
return 0;
if (arglim)
*arglim = (char *) p;
if (overflow)
*overflow = v;
return i;
}
/* Output fraction and trailing digits appropriate for a nanoseconds
@@ -456,11 +526,10 @@ code_ns_fraction (int ns, char *p)
}
char const *
code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
code_timespec (struct timespec t, char tsbuf[TIMESPEC_STRSIZE_BOUND])
{
time_t s = t.tv_sec;
int ns = t.tv_nsec;
char *np;
bool negative = s < 0;
/* ignore invalid values of ns */
@@ -473,46 +542,24 @@ code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
ns = BILLION - ns;
}
np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
if (negative)
*--np = '-';
code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
return np;
bool minus_zero = negative & !s;
char *sstr = timetostr (s, tsbuf + 1);
sstr[-1] = '-';
sstr -= minus_zero;
code_ns_fraction (ns, sstr + strlen (sstr));
return sstr;
}
struct timespec
decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
{
time_t s = TYPE_MINIMUM (time_t);
int ns = -1;
char const *p = arg;
bool negative = *arg == '-';
struct timespec r;
if (! ISDIGIT (arg[negative]))
errno = EINVAL;
else
bool overflow;
time_t s = stoint (arg, arg_lim, &overflow,
TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t));
char const *p = *arg_lim;
if (p != arg)
{
errno = 0;
if (negative)
{
intmax_t i = strtoimax (arg, arg_lim, 10);
if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i)
s = i;
else
errno = ERANGE;
}
else
{
uintmax_t i = strtoumax (arg, arg_lim, 10);
if (i <= TYPE_MAXIMUM (time_t))
s = i;
else
errno = ERANGE;
}
p = *arg_lim;
ns = 0;
if (parse_fraction && *p == '.')
@@ -520,42 +567,33 @@ decode_timespec (char const *arg, char **arg_lim, bool parse_fraction)
int digits = 0;
bool trailing_nonzero = false;
while (ISDIGIT (*++p))
while (c_isdigit (*++p))
if (digits < LOG10_BILLION)
digits++, ns = 10 * ns + (*p - '0');
else
trailing_nonzero |= *p != '0';
*arg_lim = (char *) p;
while (digits < LOG10_BILLION)
digits++, ns *= 10;
if (negative)
if (*arg == '-')
{
/* Convert "-1.10000000000001" to s == -2, ns == 89999999.
I.e., truncate time stamps towards minus infinity while
converting them to internal form. */
ns += trailing_nonzero;
if (ns != 0)
{
if (s == TYPE_MINIMUM (time_t))
ns = -1;
else
{
s--;
ns = BILLION - ns;
}
}
ns = ckd_sub (&s, s, 1) ? -1 : BILLION - ns;
}
}
if (errno == ERANGE)
if (overflow)
ns = -1;
}
*arg_lim = (char *) p;
r.tv_sec = s;
r.tv_nsec = ns;
return r;
return (struct timespec) { .tv_sec = s, .tv_nsec = ns };
}
/* File handling. */
@@ -664,9 +702,9 @@ remove_any_file (const char *file_name, enum remove_option option)
case RECURSIVE_REMOVE_OPTION:
{
char *directory = tar_savedir (file_name, 0);
char *directory = tar_savedir (file_name, false);
char const *entry;
size_t entrylen;
idx_t entrylen;
if (! directory)
return 0;
@@ -678,13 +716,11 @@ remove_any_file (const char *file_name, enum remove_option option)
char *file_name_buffer = make_file_name (file_name, entry);
int r = remove_any_file (file_name_buffer,
RECURSIVE_REMOVE_OPTION);
int e = errno;
free (file_name_buffer);
if (! r)
{
free (directory);
errno = e;
return 0;
}
}
@@ -717,14 +753,14 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
possible, real problems are unlikely. Doing any better would require a
convention, GNU-wide, for all programs doing backups. */
assign_string (&after_backup_name, 0);
assign_null (&after_backup_name);
/* Check if we really need to backup the file. */
if (this_is_the_archive && _remdev (file_name))
return true;
if (deref_stat (file_name, &file_stat) != 0)
if (deref_stat (file_name, &file_stat) < 0)
{
if (errno == ENOENT)
return true;
@@ -757,10 +793,10 @@ maybe_backup_file (const char *file_name, bool this_is_the_archive)
{
/* The backup operation failed. */
int e = errno;
ERROR ((0, e, _("%s: Cannot rename to %s"),
quotearg_colon (before_backup_name),
quote_n (1, after_backup_name)));
assign_string (&after_backup_name, 0);
paxerror (e, _("%s: Cannot rename to %s"),
quotearg_colon (before_backup_name),
quote_n (1, after_backup_name));
assign_null (&after_backup_name);
return false;
}
}
@@ -773,18 +809,18 @@ undo_last_backup (void)
if (after_backup_name)
{
if (renameat (chdir_fd, after_backup_name, chdir_fd, before_backup_name)
!= 0)
< 0)
{
int e = errno;
ERROR ((0, e, _("%s: Cannot rename to %s"),
quotearg_colon (after_backup_name),
quote_n (1, before_backup_name)));
paxerror (e, _("%s: Cannot rename to %s"),
quotearg_colon (after_backup_name),
quote_n (1, before_backup_name));
}
if (verbose_option)
fprintf (stdlis, _("Renaming %s back to %s\n"),
quote_n (0, after_backup_name),
quote_n (1, before_backup_name));
assign_string (&after_backup_name, 0);
assign_null (&after_backup_name);
}
}
@@ -800,21 +836,25 @@ deref_stat (char const *name, struct stat *buf)
/* Read from FD into the buffer BUF with COUNT bytes. Attempt to fill
BUF. Wait until input is available; this matters because files are
opened O_NONBLOCK for security reasons, and on some file systems
this can cause read to fail with errno == EAGAIN. Return the
actual number of bytes read, zero for EOF, or
SAFE_READ_ERROR upon error. */
size_t
blocking_read (int fd, void *buf, size_t count)
this can cause read to fail with errno == EAGAIN.
If returning less than COUNT, set errno to indicate the error
except set errno = 0 to indicate EOF. */
idx_t
blocking_read (int fd, void *buf, idx_t count)
{
size_t bytes = safe_read (fd, buf, count);
idx_t bytes = full_read (fd, buf, count);
#if defined F_SETFL && O_NONBLOCK
if (bytes == SAFE_READ_ERROR && errno == EAGAIN)
if (bytes < count && errno == EAGAIN)
{
int flags = fcntl (fd, F_GETFL);
if (0 <= flags && flags & O_NONBLOCK
&& fcntl (fd, F_SETFL, flags & ~O_NONBLOCK) != -1)
bytes = safe_read (fd, buf, count);
{
char *cbuf = buf;
count -= bytes;
bytes += full_read (fd, cbuf + bytes, count);
}
}
#endif
@@ -826,11 +866,11 @@ blocking_read (int fd, void *buf, size_t count)
files are opened O_NONBLOCK for security reasons, and on some file
systems this can cause write to fail with errno == EAGAIN. Return
the actual number of bytes written, setting errno if that is less
than COUNT. */
size_t
blocking_write (int fd, void const *buf, size_t count)
than COUNT. Return -1 on write error. */
idx_t
blocking_write (int fd, void const *buf, idx_t count)
{
size_t bytes = full_write (fd, buf, count);
idx_t bytes = full_write (fd, buf, count);
#if defined F_SETFL && O_NONBLOCK
if (bytes < count && errno == EAGAIN)
@@ -879,10 +919,10 @@ struct wd
static struct wd *wd;
/* The number of working directories in the vector. */
static size_t wd_count;
static idx_t wd_count;
/* The allocated size of the vector. */
static size_t wd_alloc;
static idx_t wd_alloc;
/* The maximum number of chdir targets with open directories.
Don't make it too large, as many operating systems have a small
@@ -892,36 +932,30 @@ enum { CHDIR_CACHE_SIZE = 16 };
/* Indexes into WD of chdir targets with open file descriptors, sorted
most-recently used first. Zero indexes are unused. */
static int wdcache[CHDIR_CACHE_SIZE];
static idx_t wdcache[CHDIR_CACHE_SIZE];
/* Number of nonzero entries in WDCACHE. */
static size_t wdcache_count;
static idx_t wdcache_count;
int
idx_t
chdir_count (void)
{
if (wd_count == 0)
return wd_count;
return wd_count - 1;
return wd_count - !!wd_count;
}
/* DIR is the operand of a -C option; add it to vector of chdir targets,
and return the index of its location. */
int
idx_t
chdir_arg (char const *dir)
{
char *absdir;
if (wd_count == wd_alloc)
{
if (wd_alloc == 0)
wd_alloc = 2;
wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
wd = xpalloc (wd, &wd_alloc, wd_alloc ? 1 : 2, -1, sizeof *wd);
if (! wd_count)
{
wd[wd_count].name = ".";
wd[wd_count].abspath = xgetcwd ();
wd[wd_count].abspath = NULL;
wd[wd_count].fd = AT_FDCWD;
wd_count++;
}
@@ -938,28 +972,14 @@ chdir_arg (char const *dir)
return wd_count - 1;
}
/* If the given name is absolute, use it to represent this directory;
otherwise, construct a name based on the previous -C option. */
if (IS_ABSOLUTE_FILE_NAME (dir))
absdir = xstrdup (dir);
else if (wd[wd_count - 1].abspath)
{
namebuf_t nbuf = namebuf_create (wd[wd_count - 1].abspath);
namebuf_add_dir (nbuf, dir);
absdir = namebuf_finish (nbuf);
}
else
absdir = 0;
wd[wd_count].name = dir;
wd[wd_count].abspath = absdir;
wd[wd_count].abspath = NULL;
wd[wd_count].fd = 0;
return wd_count++;
}
/* Index of current directory. */
int chdir_current;
idx_t chdir_current;
/* Value suitable for use as the first argument to openat, and in
similar locations for fstatat, etc. This is an open file
@@ -973,7 +993,7 @@ int chdir_fd = AT_FDCWD;
working directory; otherwise, I must be a value returned by
chdir_arg. */
void
chdir_do (int i)
chdir_do (idx_t i)
{
if (chdir_current != i)
{
@@ -998,7 +1018,7 @@ chdir_do (int i)
else
{
struct wd *stale = &wd[wdcache[CHDIR_CACHE_SIZE - 1]];
if (close (stale->fd) != 0)
if (close (stale->fd) < 0)
close_diag (stale->name);
stale->fd = 0;
wdcache[CHDIR_CACHE_SIZE - 1] = i;
@@ -1009,11 +1029,11 @@ chdir_do (int i)
{
/* Move the i value to the front of the cache. This is
O(CHDIR_CACHE_SIZE), but the cache is small. */
size_t ci;
int prev = wdcache[0];
idx_t ci;
idx_t prev = wdcache[0];
for (ci = 1; prev != i; ci++)
{
int cur = wdcache[ci];
idx_t cur = wdcache[ci];
wdcache[ci] = prev;
if (cur == i)
break;
@@ -1041,15 +1061,50 @@ tar_dirname (void)
process's actual cwd. (Note that in this case IDX is ignored,
since it should always be 0.) */
static const char *
tar_getcdpath (int idx)
tar_getcdpath (idx_t idx)
{
if (!wd)
{
static char *cwd;
if (!cwd)
cwd = xgetcwd ();
{
cwd = xgetcwd ();
if (!cwd)
call_arg_fatal ("getcwd", ".");
}
return cwd;
}
if (!wd[idx].abspath)
{
idx_t save_cwdi = chdir_current, i = idx;
while (0 < i && !wd[i - 1].abspath)
i--;
for (; i <= idx; i++)
{
chdir_do (i);
if (i == 0)
{
if ((wd[i].abspath = xgetcwd ()) == NULL)
call_arg_fatal ("getcwd", ".");
}
else if (IS_ABSOLUTE_FILE_NAME (wd[i].name))
/* If the given name is absolute, use it to represent this
directory; otherwise, construct a name based on the
previous -C option. */
wd[i].abspath = xstrdup (wd[i].name);
else
{
namebuf_t nbuf = namebuf_create (wd[i - 1].abspath);
namebuf_add_dir (nbuf, wd[i].name);
wd[i].abspath = namebuf_finish (nbuf);
}
}
chdir_do (save_cwdi);
}
return wd[idx].abspath;
}
@@ -1058,7 +1113,7 @@ close_diag (char const *name)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
close_warn (name);
}
else
@@ -1070,7 +1125,7 @@ open_diag (char const *name)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
open_warn (name);
}
else
@@ -1078,11 +1133,11 @@ open_diag (char const *name)
}
void
read_diag_details (char const *name, off_t offset, size_t size)
read_diag_details (char const *name, off_t offset, idx_t size)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
read_warn_details (name, offset, size);
}
else
@@ -1094,7 +1149,7 @@ readlink_diag (char const *name)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
readlink_warn (name);
}
else
@@ -1106,7 +1161,7 @@ savedir_diag (char const *name)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
savedir_warn (name);
}
else
@@ -1118,7 +1173,7 @@ seek_diag_details (char const *name, off_t offset)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
seek_warn_details (name, offset);
}
else
@@ -1130,7 +1185,7 @@ stat_diag (char const *name)
{
if (ignore_failed_read_option)
{
if (WARNING_ENABLED(WARN_FAILED_READ))
if (warning_enabled (WARN_FAILED_READ))
stat_warn (name);
}
else
@@ -1143,9 +1198,8 @@ file_removed_diag (const char *name, bool top_level,
{
if (!top_level && errno == ENOENT)
{
WARNOPT (WARN_FILE_REMOVED,
(0, 0, _("%s: File removed before we read it"),
quotearg_colon (name)));
warnopt (WARN_FILE_REMOVED, 0, _("%s: File removed before we read it"),
quotearg_colon (name));
set_exit_status (TAREXIT_DIFFERS);
}
else
@@ -1157,7 +1211,7 @@ pid_t
xfork (void)
{
pid_t p = fork ();
if (p == (pid_t) -1)
if (p < 0)
call_arg_fatal ("fork", _("child process"));
return p;
}
@@ -1170,40 +1224,13 @@ xpipe (int fd[2])
call_arg_fatal ("pipe", _("interprocess channel"));
}
/* Return PTR, aligned upward to the next multiple of ALIGNMENT.
ALIGNMENT must be nonzero. The caller must arrange for ((char *)
PTR) through ((char *) PTR + ALIGNMENT - 1) to be addressable
locations. */
static inline void *
ptr_align (void *ptr, size_t alignment)
{
char *p0 = ptr;
char *p1 = p0 + alignment - 1;
return p1 - (size_t) p1 % alignment;
}
/* Return the address of a page-aligned buffer of at least SIZE bytes.
The caller should free *PTR when done with the buffer. */
void *
page_aligned_alloc (void **ptr, size_t size)
{
size_t alignment = getpagesize ();
size_t size1 = size + alignment;
if (size1 < size)
xalloc_die ();
*ptr = xmalloc (size1);
return ptr_align (*ptr, alignment);
}
struct namebuf
{
char *buffer; /* directory, '/', and directory member */
size_t buffer_size; /* allocated size of name_buffer */
size_t dir_length; /* length of directory part in buffer */
idx_t buffer_size; /* allocated size of name_buffer */
idx_t dir_length; /* length of directory part in buffer */
};
namebuf_t
@@ -1229,9 +1256,10 @@ namebuf_free (namebuf_t buf)
char *
namebuf_name (namebuf_t buf, const char *name)
{
size_t len = strlen (name);
while (buf->dir_length + len + 1 >= buf->buffer_size)
buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
idx_t len = strlen (name);
ptrdiff_t incr_min = buf->dir_length + len + 2 - buf->buffer_size;
if (0 < incr_min)
buf->buffer = xpalloc (buf->buffer, &buf->buffer_size, incr_min, -1, 1);
strcpy (buf->buffer + buf->dir_length, name);
return buf->buffer;
}
@@ -1267,7 +1295,7 @@ namebuf_finish (namebuf_t buf)
Return NULL on errors.
*/
char *
tar_savedir (const char *name, int must_exist)
tar_savedir (const char *name, bool must_exist)
{
char *ret = NULL;
DIR *dir = NULL;
@@ -1282,7 +1310,7 @@ tar_savedir (const char *name, int must_exist)
&& (ret = streamsavedir (dir, savedir_sort_order))))
savedir_error (name);
if (dir ? closedir (dir) != 0 : 0 <= fd && close (fd) != 0)
if (dir ? closedir (dir) < 0 : 0 <= fd && close (fd) < 0)
savedir_error (name);
return ret;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/* System-dependent calls for tar.
Copyright 2003-2019 Free Software Foundation, Inc.
Copyright 2003-2025 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -17,11 +17,21 @@
#include <system.h>
#if HAVE_SYS_MTIO_H
# include <sys/ioctl.h>
# include <sys/mtio.h>
#endif
#include "common.h"
#include <priv-set.h>
#include <rmt.h>
#include <same-inode.h>
#include <signal.h>
#include <wordsplit.h>
#include <poll.h>
#include <parse-datetime.h>
bool dev_null_output;
static _Noreturn void
xexec (const char *cmd)
@@ -37,7 +47,36 @@ xexec (const char *cmd)
exec_fatal (cmd);
}
#if MSDOS
/* True if the archive is seekable via ioctl and MTIOCTOP,
or if it is not known whether it is seekable.
False if it is known to be not seekable. */
static bool mtioseekable_archive;
bool
mtioseek (bool count_files, off_t count)
{
if (mtioseekable_archive)
{
#ifdef MTIOCTOP
struct mtop operation;
operation.mt_op = (count_files
? (count < 0 ? MTBSF : MTFSF)
: (count < 0 ? MTBSR : MTFSR));
if (! (count < 0
? ckd_sub (&operation.mt_count, 0, count)
: ckd_add (&operation.mt_count, count, 0))
&& (0 <= rmtioctl (archive, MTIOCTOP, &operation)
|| (errno == EIO
&& 0 <= rmtioctl (archive, MTIOCTOP, &operation))))
return true;
#endif
mtioseekable_archive = false;
}
return false;
}
#if !HAVE_WAITPID /* MingW, MSVC 14. */
bool
sys_get_archive_stat (void)
@@ -51,11 +90,6 @@ sys_file_is_archive (struct tar_stat_info *p)
return false;
}
void
sys_save_archive_dev_ino (void)
{
}
void
sys_detect_dev_null_output (void)
{
@@ -92,82 +126,86 @@ sys_compare_gid (struct stat *a, struct stat *b)
return true;
}
void
sys_compare_links (struct stat *link_data, struct stat *stat_data)
{
return true;
}
int
sys_truncate (int fd)
{
return write (fd, "", 0);
}
size_t
idx_t
sys_write_archive_buffer (void)
{
return full_write (archive, record_start->buffer, record_size);
return full_write (archive, charptr (record_start), record_size);
}
/* Set ARCHIVE for writing, then compressing an archive. */
void
sys_child_open_for_compress (void)
{
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
paxfatal (0, _("Cannot use compressed or remote archives"));
}
/* Set ARCHIVE for uncompressing, then reading an archive. */
void
sys_child_open_for_uncompress (void)
{
FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
paxfatal (0, _("Cannot use compressed or remote archives"));
}
bool
sys_exec_setmtime_script (const char *script_name,
int dirfd,
const char *file_name,
const char *fmt,
struct timespec *ts)
{
paxfatal (0, _("--set-mtime-command not implemented on this platform"));
}
#else
extern union block *record_start; /* FIXME */
static struct stat archive_stat; /* stat block for archive file */
bool
sys_get_archive_stat (void)
{
return fstat (archive, &archive_stat) == 0;
bool remote = _isrmt (archive);
mtioseekable_archive = true;
if (!remote && 0 <= archive && fstat (archive, &archive_stat) == 0)
{
if (!S_ISCHR (archive_stat.st_mode))
mtioseekable_archive = false;
return true;
}
else
{
/* FIXME: This memset should not be needed. It is present only
because other parts of tar may incorrectly access
archive_stat even if it's not the archive status. */
memset (&archive_stat, 0, sizeof archive_stat);
return remote;
}
}
bool
sys_file_is_archive (struct tar_stat_info *p)
{
return (ar_dev && p->stat.st_dev == ar_dev && p->stat.st_ino == ar_ino);
return (!dev_null_output && !_isrmt (archive)
&& psame_inode (&p->stat, &archive_stat));
}
/* Save archive file inode and device numbers */
void
sys_save_archive_dev_ino (void)
{
if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode))
{
ar_dev = archive_stat.st_dev;
ar_ino = archive_stat.st_ino;
}
else
ar_dev = 0;
}
static char const dev_null[] = "/dev/null";
/* Detect if outputting to "/dev/null". */
void
sys_detect_dev_null_output (void)
{
static char const dev_null[] = "/dev/null";
struct stat dev_null_stat;
static struct stat dev_null_stat;
dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0
|| (! _isrmt (archive)
&& S_ISCHR (archive_stat.st_mode)
&& stat (dev_null, &dev_null_stat) == 0
&& archive_stat.st_dev == dev_null_stat.st_dev
&& archive_stat.st_ino == dev_null_stat.st_ino));
&& (dev_null_stat.st_ino != 0
|| stat (dev_null, &dev_null_stat) == 0)
&& psame_inode (&archive_stat, &dev_null_stat)));
}
void
@@ -177,7 +215,7 @@ sys_wait_for_child (pid_t child_pid, bool eof)
{
int wait_status;
while (waitpid (child_pid, &wait_status, 0) == -1)
while (waitpid (child_pid, &wait_status, 0) < 0)
if (errno != EINTR)
{
waitpid_error (use_compress_program_option);
@@ -188,11 +226,11 @@ sys_wait_for_child (pid_t child_pid, bool eof)
{
int sig = WTERMSIG (wait_status);
if (!(!eof && sig == SIGPIPE))
FATAL_ERROR ((0, 0, _("Child died with signal %d"), sig));
paxfatal (0, _("Child died with signal %d"), sig);
}
else if (WEXITSTATUS (wait_status) != 0)
FATAL_ERROR ((0, 0, _("Child returned status %d"),
WEXITSTATUS (wait_status)));
paxfatal (0, _("Child returned status %d"),
WEXITSTATUS (wait_status));
}
}
@@ -213,7 +251,7 @@ sys_spawn_shell (void)
else
{
int wait_status;
while (waitpid (child, &wait_status, 0) == -1)
while (waitpid (child, &wait_status, 0) < 0)
if (errno != EINTR)
{
waitpid_error (shell);
@@ -234,41 +272,39 @@ sys_compare_gid (struct stat *a, struct stat *b)
return a->st_gid == b->st_gid;
}
bool
sys_compare_links (struct stat *link_data, struct stat *stat_data)
{
return stat_data->st_dev == link_data->st_dev
&& stat_data->st_ino == link_data->st_ino;
}
int
sys_truncate (int fd)
{
off_t pos = lseek (fd, (off_t) 0, SEEK_CUR);
off_t pos = lseek (fd, 0, SEEK_CUR);
return pos < 0 ? -1 : ftruncate (fd, pos);
}
/* Return nonzero if NAME is the name of a regular file, or if the file
/* Return true if NAME is the name of a regular file, or if the file
does not exist (so it would be created as a regular file). */
static int
static bool
is_regular_file (const char *name)
{
struct stat stbuf;
if (stat (name, &stbuf) == 0)
return S_ISREG (stbuf.st_mode);
return !!S_ISREG (stbuf.st_mode);
else
return errno == ENOENT;
}
size_t
idx_t
sys_write_archive_buffer (void)
{
return rmtwrite (archive, record_start->buffer, record_size);
return rmtwrite (archive, charptr (record_start), record_size);
}
#define PREAD 0 /* read file descriptor from pipe() */
#define PWRITE 1 /* write file descriptor from pipe() */
/* Read and write file descriptors from a pipe(pipefd) call. */
enum { PREAD, PWRITE };
/* Work around GCC bug 109839. */
#if 13 <= __GNUC__
# pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
#endif
/* Duplicate file descriptor FROM into becoming INTO.
INTO is closed first and has to be the next available slot. */
@@ -277,37 +313,20 @@ xdup2 (int from, int into)
{
if (from != into)
{
int status = close (into);
if (status != 0 && errno != EBADF)
{
int e = errno;
FATAL_ERROR ((0, e, _("Cannot close")));
}
status = dup (from);
if (status != into)
{
if (status < 0)
{
int e = errno;
FATAL_ERROR ((0, e, _("Cannot dup")));
}
abort ();
}
if (dup2 (from, into) < 0)
paxfatal (errno, _("Cannot dup2"));
xclose (from);
}
}
static void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
/* Propagate any failure of the grandchild back to the parent. */
static void
static _Noreturn void
wait_for_grandchild (pid_t pid)
{
int wait_status;
int exit_code = 0;
while (waitpid (pid, &wait_status, 0) == -1)
while (waitpid (pid, &wait_status, 0) < 0)
if (errno != EINTR)
{
waitpid_error (use_compress_program_option);
@@ -419,20 +438,20 @@ sys_child_open_for_compress (void)
while (1)
{
size_t status = 0;
ptrdiff_t status = 0;
char *cursor;
size_t length;
idx_t length;
/* Assemble a record. */
for (length = 0, cursor = record_start->buffer;
for (length = 0, cursor = charptr (record_start);
length < record_size;
length += status, cursor += status)
{
size_t size = record_size - length;
idx_t size = record_size - length;
status = safe_read (STDIN_FILENO, cursor, size);
if (status == SAFE_READ_ERROR)
if (status < 0)
read_fatal (use_compress_program_option);
if (status == 0)
break;
@@ -448,7 +467,7 @@ sys_child_open_for_compress (void)
if (length > 0)
{
memset (record_start->buffer + length, 0, record_size - length);
memset (charptr (record_start) + length, 0, record_size - length);
status = sys_write_archive_buffer ();
if (status != record_size)
archive_write_error (status);
@@ -481,24 +500,22 @@ run_decompress_program (void)
{
if (prog)
{
WARNOPT (WARN_DECOMPRESS_PROGRAM,
(0, errno, _("cannot run %s"), prog));
WARNOPT (WARN_DECOMPRESS_PROGRAM,
(0, 0, _("trying %s"), p));
warnopt (WARN_DECOMPRESS_PROGRAM, errno, _("cannot run %s"), prog);
warnopt (WARN_DECOMPRESS_PROGRAM, 0, _("trying %s"), p);
}
if (wordsplit (p, &ws, wsflags))
FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
p, wordsplit_strerror (&ws)));
if (wordsplit (p, &ws, wsflags) != WRDSE_OK)
paxfatal (0, _("cannot split string '%s': %s"),
p, wordsplit_strerror (&ws));
wsflags |= WRDSF_REUSE;
memmove(ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
sizeof(ws.ws_wordv[0])*ws.ws_wordc);
memmove (ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
ws.ws_wordc * sizeof *ws.ws_wordv);
ws.ws_wordv[ws.ws_wordc] = (char *) "-d";
prog = p;
execvp (ws.ws_wordv[0], ws.ws_wordv);
ws.ws_wordv[ws.ws_wordc] = NULL;
}
if (!prog)
FATAL_ERROR ((0, 0, _("unable to run decompression program")));
paxfatal (0, _("unable to run decompression program"));
exec_fatal (prog);
}
@@ -540,7 +557,7 @@ sys_child_open_for_uncompress (void)
&& !_remdev (archive_name_array[0])
&& is_regular_file (archive_name_array[0]))
{
/* We don't need a grandchild tar. Open the archive and lauch the
/* We don't need a grandchild tar. Open the archive and launch the
uncompressor. */
archive = open (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW);
@@ -586,34 +603,26 @@ sys_child_open_for_uncompress (void)
/* Let's read the archive and pipe it into stdout. */
while (1)
while (true)
{
char *cursor;
size_t maximum;
size_t count;
size_t status;
clear_read_error_count ();
error_loop:
status = rmtread (archive, record_start->buffer, record_size);
if (status == SAFE_READ_ERROR)
{
archive_read_error ();
goto error_loop;
}
if (status == 0)
ptrdiff_t n;
while ((n = rmtread (archive, charptr (record_start), record_size)) < 0)
archive_read_error ();
if (n == 0)
break;
cursor = record_start->buffer;
maximum = status;
while (maximum)
char *cursor = charptr (record_start);
do
{
count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE;
idx_t count = min (n, BLOCKSIZE);
if (full_write (STDOUT_FILENO, cursor, count) != count)
write_error (use_compress_program_option);
cursor += count;
maximum -= count;
n -= count;
}
while (n);
}
xclose (STDOUT_FILENO);
@@ -626,11 +635,8 @@ sys_child_open_for_uncompress (void)
static void
dec_to_env (char const *envar, uintmax_t num)
{
char buf[UINTMAX_STRSIZE_BOUND];
char *numstr;
numstr = STRINGIFY_BIGINT (num, buf);
if (setenv (envar, numstr, 1) != 0)
char numstr[UINTMAX_STRSIZE_BOUND];
if (setenv (envar, umaxtostr (num, numstr), 1) < 0)
xalloc_die ();
}
@@ -638,17 +644,19 @@ static void
time_to_env (char const *envar, struct timespec t)
{
char buf[TIMESPEC_STRSIZE_BOUND];
if (setenv (envar, code_timespec (t, buf), 1) != 0)
if (setenv (envar, code_timespec (t, buf), 1) < 0)
xalloc_die ();
}
static void
oct_to_env (char const *envar, unsigned long num)
oct_to_env (char const *envar, mode_t m)
{
char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
snprintf (buf, sizeof buf, "0%lo", num);
if (setenv (envar, buf, 1) != 0)
char buf[sizeof "0" + (UINTMAX_WIDTH + 2) / 3];
uintmax_t um = m;
if (EXPR_SIGNED (m) && sizeof m < sizeof um)
um &= ~ (UINTMAX_MAX << TYPE_WIDTH (m));
sprintf (buf, "%#"PRIoMAX, um);
if (setenv (envar, buf, 1) < 0)
xalloc_die ();
}
@@ -657,7 +665,7 @@ str_to_env (char const *envar, char const *str)
{
if (str)
{
if (setenv (envar, str, 1) != 0)
if (setenv (envar, str, 1) < 0)
xalloc_die ();
}
else
@@ -670,7 +678,7 @@ chr_to_env (char const *envar, char c)
char buf[2];
buf[0] = c;
buf[1] = 0;
if (setenv (envar, buf, 1) != 0)
if (setenv (envar, buf, 1) < 0)
xalloc_die ();
}
@@ -725,7 +733,7 @@ static pid_t global_pid;
static void (*pipe_handler) (int sig);
int
sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
sys_exec_command (char *file_name, char typechar, struct tar_stat_info *st)
{
int p[2];
@@ -758,7 +766,7 @@ sys_wait_command (void)
return;
signal (SIGPIPE, pipe_handler);
while (waitpid (global_pid, &status, 0) == -1)
while (waitpid (global_pid, &status, 0) < 0)
if (errno != EINTR)
{
global_pid = -1;
@@ -769,53 +777,65 @@ sys_wait_command (void)
if (WIFEXITED (status))
{
if (!ignore_command_error_option && WEXITSTATUS (status))
ERROR ((0, 0, _("%lu: Child returned status %d"),
(unsigned long) global_pid, WEXITSTATUS (status)));
paxerror (0, _("%jd: Child returned status %d"),
intmax (global_pid), WEXITSTATUS (status));
}
else if (WIFSIGNALED (status))
{
WARN ((0, 0, _("%lu: Child terminated on signal %d"),
(unsigned long) global_pid, WTERMSIG (status)));
paxwarn (0, _("%jd: Child terminated on signal %d"),
intmax (global_pid), WTERMSIG (status));
}
else
ERROR ((0, 0, _("%lu: Child terminated on unknown reason"),
(unsigned long) global_pid));
paxerror (0, _("%jd: Child terminated on unknown reason"),
intmax (global_pid));
global_pid = -1;
}
int
sys_exec_info_script (const char **archive_name, int volume_number)
sys_exec_info_script (const char **archive_name, intmax_t volume_number)
{
pid_t pid;
char uintbuf[UINTMAX_STRSIZE_BOUND];
int p[2];
static void (*saved_handler) (int sig);
xpipe (p);
saved_handler = signal (SIGPIPE, SIG_IGN);
pid = xfork ();
pid_t pid = xfork ();
if (pid != 0)
{
/* Master */
int rc;
int status;
char *buf = NULL;
size_t size = 0;
FILE *fp;
xclose (p[PWRITE]);
fp = fdopen (p[PREAD], "r");
rc = getline (&buf, &size, fp);
fclose (fp);
FILE *fp = fdopen (p[PREAD], "r");
if (!fp)
{
signal (SIGPIPE, saved_handler);
call_arg_error ("fdopen", info_script_option);
return -1;
}
ssize_t rc = getline (&buf, &size, fp);
if (rc < 0)
{
signal (SIGPIPE, saved_handler);
read_error (info_script_option);
return -1;
}
*archive_name = buf;
buf[rc - 1] = '\0';
if (fclose (fp) < 0)
{
signal (SIGPIPE, saved_handler);
close_error (info_script_option);
return -1;
}
if (rc > 0 && buf[rc-1] == '\n')
buf[--rc] = 0;
while (waitpid (pid, &status, 0) == -1)
while (waitpid (pid, &status, 0) < 0)
if (errno != EINTR)
{
signal (SIGPIPE, saved_handler);
@@ -824,31 +844,19 @@ sys_exec_info_script (const char **archive_name, int volume_number)
}
signal (SIGPIPE, saved_handler);
if (WIFEXITED (status))
{
if (WEXITSTATUS (status) == 0 && rc > 0)
*archive_name = buf;
else
free (buf);
return WEXITSTATUS (status);
}
free (buf);
return -1;
return WIFEXITED (status) ? WEXITSTATUS (status) : -1;
}
/* Child */
setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
setenv ("TAR_ARCHIVE", *archive_name, 1);
setenv ("TAR_VOLUME", STRINGIFY_BIGINT (volume_number, uintbuf), 1);
setenv ("TAR_BLOCKING_FACTOR",
STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
str_to_env ("TAR_VERSION", PACKAGE_VERSION);
str_to_env ("TAR_ARCHIVE", *archive_name);
dec_to_env ("TAR_VOLUME", volume_number);
dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
setenv ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT ?
archive_format : current_format), 1);
setenv ("TAR_FD", STRINGIFY_BIGINT (p[PWRITE], uintbuf), 1);
dec_to_env ("TAR_FD", p[PWRITE]);
xclose (p[PREAD]);
@@ -859,12 +867,9 @@ sys_exec_info_script (const char **archive_name, int volume_number)
void
sys_exec_checkpoint_script (const char *script_name,
const char *archive_name,
int checkpoint_number)
intmax_t checkpoint_number)
{
pid_t pid;
char uintbuf[UINTMAX_STRSIZE_BOUND];
pid = xfork ();
pid_t pid = xfork ();
if (pid != 0)
{
@@ -872,7 +877,7 @@ sys_exec_checkpoint_script (const char *script_name,
int status;
while (waitpid (pid, &status, 0) == -1)
while (waitpid (pid, &status, 0) < 0)
if (errno != EINTR)
{
waitpid_error (script_name);
@@ -883,17 +888,175 @@ sys_exec_checkpoint_script (const char *script_name,
}
/* Child */
setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
setenv ("TAR_ARCHIVE", archive_name, 1);
setenv ("TAR_CHECKPOINT", STRINGIFY_BIGINT (checkpoint_number, uintbuf), 1);
setenv ("TAR_BLOCKING_FACTOR",
STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
setenv ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT ?
archive_format : current_format), 1);
str_to_env ("TAR_VERSION", PACKAGE_VERSION);
str_to_env ("TAR_ARCHIVE", archive_name);
dec_to_env ("TAR_CHECKPOINT", checkpoint_number);
dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
str_to_env ("TAR_SUBCOMMAND", subcommand_string (subcommand_option));
str_to_env ("TAR_FORMAT",
archive_format_string (current_format == DEFAULT_FORMAT
? archive_format : current_format));
priv_set_restore_linkdir ();
xexec (script_name);
}
bool
sys_exec_setmtime_script (const char *script_name,
int dirfd,
const char *file_name,
const char *fmt,
struct timespec *ts)
{
pid_t pid;
int p[2];
bool stop = false;
struct pollfd pfd;
char *buffer = NULL;
idx_t buflen = 0;
idx_t bufsize = 0;
char *cp;
bool rc = true;
if (pipe (p) < 0)
paxfatal (errno, _("pipe failed"));
if ((pid = xfork ()) == 0)
{
char *command = xmalloc (strlen (script_name) + strlen (file_name) + 2);
strcpy (command, script_name);
strcat (command, " ");
strcat (command, file_name);
if (dirfd != AT_FDCWD && fchdir (dirfd) < 0)
paxfatal (errno, _("chdir failed"));
close (p[0]);
if (dup2 (p[1], STDOUT_FILENO) < 0)
paxfatal (errno, _("dup2 failed"));
if (p[1] != STDOUT_FILENO)
close (p[1]);
close (STDIN_FILENO);
if (open (dev_null, O_RDONLY) != STDIN_FILENO)
open_error (dev_null);
priv_set_restore_linkdir ();
/* FIXME: This mishandles shell metacharacters in the file name.
Come to think of it, isn't every use of xexec suspect? */
xexec (command);
}
close (p[1]);
pfd.fd = p[0];
pfd.events = POLLIN;
while (1)
{
int n = poll (&pfd, 1, -1);
if (n < 0)
{
if (errno != EINTR)
{
paxerror (errno, _("poll failed"));
stop = true;
break;
}
}
if (n == 0)
break;
if (pfd.revents & POLLIN)
{
if (buflen == bufsize)
buffer = xpalloc (buffer, &bufsize, 1, -1, 1);
ssize_t nread = read (pfd.fd, buffer + buflen, bufsize - buflen);
if (nread < 0)
{
paxerror (errno, _("error reading output of %s"), script_name);
stop = true;
break;
}
if (nread == 0)
break;
buflen += n;
}
else if (pfd.revents & POLLHUP)
break;
}
close (pfd.fd);
if (stop)
kill (SIGKILL, pid);
sys_wait_for_child (pid, false);
if (stop)
{
free (buffer);
return false;
}
if (buflen == 0)
{
paxerror (0, _("empty output from \"%s %s\""), script_name, file_name);
return false;
}
cp = memchr (buffer, '\n', buflen);
if (cp)
*cp = 0;
else
{
if (buflen == bufsize)
buffer = xirealloc (buffer, ++bufsize);
buffer[buflen] = 0;
}
if (fmt)
{
struct tm tm;
time_t t;
cp = strptime (buffer, fmt, &tm);
if (cp == NULL)
{
paxerror (0, _("output from \"%s %s\" does not satisfy format string:"
" %s"),
script_name, file_name, buffer);
rc = false;
}
else if (*cp != 0)
{
paxwarn (0, _("unconsumed output from \"%s %s\": %s"),
script_name, file_name, cp);
rc = false;
}
else
{
tm.tm_wday = -1;
t = mktime (&tm);
if (tm.tm_wday < 0)
{
paxerror (errno, _("mktime failed"));
rc = false;
}
else
{
ts->tv_sec = t;
ts->tv_nsec = 0;
}
}
}
else if (! parse_datetime (ts, buffer, NULL))
{
paxerror (0, _("unparsable output from \"%s %s\": %s"),
script_name, file_name, buffer);
rc = false;
}
free (buffer);
return rc;
}
#endif /* not MSDOS */

1415
src/tar.c

File diff suppressed because it is too large Load Diff

139
src/tar.h
View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/* Update a tar archive.
Copyright 1988-2019 Free Software Foundation, Inc.
Copyright 1988-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -26,10 +26,6 @@
#include <quotearg.h>
#include "common.h"
/* FIXME: This module should not directly handle the following variable,
instead, this should be done in buffer.c only. */
extern union block *current_block;
/* We've hit the end of the old stuff, and its time to start writing new
stuff to the tape. This involves seeking back one record and
re-writing the current record (which has been changed).
@@ -42,13 +38,14 @@ bool time_to_start_writing;
first part of the record. */
char *output_start;
static bool acting_as_filter;
/* Catenate file FILE_NAME to the archive without creating a header for it.
It had better be a tar file or the archive is screwed. */
static void
append_file (char *file_name)
{
int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
struct stat stat_data;
if (handle < 0)
{
@@ -56,49 +53,44 @@ append_file (char *file_name)
return;
}
if (fstat (handle, &stat_data) != 0)
stat_error (file_name);
else
while (true)
{
off_t bytes_left = stat_data.st_size;
while (bytes_left > 0)
{
union block *start = find_next_block ();
size_t buffer_size = available_space_after (start);
size_t status;
char buf[UINTMAX_STRSIZE_BOUND];
if (bytes_left < buffer_size)
{
buffer_size = bytes_left;
status = buffer_size % BLOCKSIZE;
if (status)
memset (start->buffer + bytes_left, 0, BLOCKSIZE - status);
}
status = safe_read (handle, start->buffer, buffer_size);
if (status == SAFE_READ_ERROR)
read_fatal_details (file_name, stat_data.st_size - bytes_left,
buffer_size);
if (status == 0)
FATAL_ERROR ((0, 0,
ngettext ("%s: File shrank by %s byte",
"%s: File shrank by %s bytes",
bytes_left),
quotearg_colon (file_name),
STRINGIFY_BIGINT (bytes_left, buf)));
bytes_left -= status;
set_next_block_after (start + (status - 1) / BLOCKSIZE);
}
union block *start = find_next_block ();
idx_t bufsize = available_space_after (start);
idx_t status = full_read (handle, charptr (start), bufsize);
if (status < bufsize && errno)
read_fatal (file_name);
if (status == 0)
break;
idx_t rem = status % BLOCKSIZE;
if (rem)
memset (charptr (start) + (status - rem), 0, BLOCKSIZE - rem);
set_next_block_after (charptr (start) + status - 1);
}
if (close (handle) != 0)
if (close (handle) < 0)
close_error (file_name);
}
/* If NAME is not a pattern, remove it from the namelist. Otherwise,
remove the FILE_NAME that matched it. Take care to look for exact
match when removing it. */
static void
remove_exact_name (struct name *name, char const *file_name)
{
if (name->is_wildcard)
{
struct name *match = name_scan (file_name, true);
name->found_count++;
if (match)
name = match;
else
return;
}
remname (name);
}
/* Implement the 'r' (add files to end of archive), and 'u' (add files
to end of archive if they aren't there, or are more up to date than
the version in the archive) commands. */
@@ -110,6 +102,7 @@ update_archive (void)
name_gather ();
open_archive (ACCESS_UPDATE);
acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
xheader_forbid_global ();
while (!found_end)
@@ -129,13 +122,13 @@ update_archive (void)
struct name *name;
decode_header (current_header, &current_stat_info,
&current_format, 0);
&current_format, false);
transform_stat_info (current_header->header.typeflag,
&current_stat_info);
archive_format = current_format;
if (subcommand_option == UPDATE_SUBCOMMAND
&& (name = name_scan (current_stat_info.file_name)) != NULL)
&& (name = name_scan (current_stat_info.file_name, false)) != NULL)
{
struct stat s;
@@ -144,10 +137,12 @@ update_archive (void)
{
if (S_ISDIR (s.st_mode))
{
char *p, *dirp = tar_savedir (name->name, 1);
char *p;
char *dirp = tar_savedir (current_stat_info.file_name,
true);
if (dirp)
{
namebuf_t nbuf = namebuf_create (name->name);
namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
for (p = dirp; *p; p += strlen (p) + 1)
addname (namebuf_name (nbuf, p),
@@ -156,17 +151,22 @@ update_archive (void)
namebuf_free (nbuf);
free (dirp);
remname (name);
remove_exact_name (name, current_stat_info.file_name);
}
}
else if (tar_timespec_cmp (get_stat_mtime (&s),
current_stat_info.mtime)
<= 0)
remname (name);
{
remove_exact_name (name, current_stat_info.file_name);
}
else if (name->is_wildcard)
addname (current_stat_info.file_name,
name->change_dir, false, NULL);
}
}
skip_member ();
skim_member (acting_as_filter);
break;
}
@@ -184,11 +184,11 @@ update_archive (void)
switch (previous_status)
{
case HEADER_STILL_UNREAD:
WARN ((0, 0, _("This does not look like a tar archive")));
paxwarn (0, _("This does not look like a tar archive"));
FALLTHROUGH;
case HEADER_SUCCESS:
case HEADER_ZERO_BLOCK:
ERROR ((0, 0, _("Skipping to next header")));
paxerror (0, _("Skipping to next header"));
FALLTHROUGH;
case HEADER_FAILURE:
break;
@@ -206,7 +206,7 @@ update_archive (void)
reset_eof ();
time_to_start_writing = true;
output_start = current_block->buffer;
output_start = charptr (current_block);
{
struct name const *p;

View File

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

View File

@@ -1,6 +1,6 @@
/* Warnings for GNU tar.
Copyright 2009-2019 Free Software Foundation, Inc.
Copyright 2009-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -48,6 +48,9 @@ static char const *const warning_args[] = {
"xattr-write",
"record-size",
"failed-read",
"missing-zero-blocks",
"verbose",
"empty-transform",
NULL
};
@@ -76,17 +79,20 @@ static int warning_types[] = {
WARN_EXISTING_FILE,
WARN_XATTR_WRITE,
WARN_RECORD_SIZE,
WARN_FAILED_READ
WARN_FAILED_READ,
WARN_MISSING_ZERO_BLOCKS,
WARN_VERBOSE_WARNINGS,
WARN_EMPTY_TRANSFORM
};
ARGMATCH_VERIFY (warning_args, warning_types);
int warning_option = WARN_ALL;
int warning_option = WARN_ALL & ~(WARN_VERBOSE_WARNINGS|WARN_MISSING_ZERO_BLOCKS);
void
set_warning_option (const char *arg)
{
int negate = 0;
bool negate = false;
int option;
if (strcmp (arg, "none") == 0)
@@ -96,7 +102,7 @@ set_warning_option (const char *arg)
}
if (strlen (arg) > 2 && memcmp (arg, "no-", 3) == 0)
{
negate = 1;
negate = true;
arg += 3;
}
@@ -107,3 +113,17 @@ set_warning_option (const char *arg)
else
warning_option |= option;
}
void
warnopt (int opt, int errnum, char const *format, ...)
{
if (warning_enabled (opt))
{
if (error_hook)
error_hook ();
va_list ap;
va_start (ap, format);
verror (0, errnum, format, ap);
va_end (ap);
}
}

View File

@@ -1,6 +1,6 @@
/* Support for extended attributes.
Copyright (C) 2006-2019 Free Software Foundation, Inc.
Copyright (C) 2006-2025 Free Software Foundation, Inc.
This file is part of GNU tar.
@@ -30,11 +30,82 @@
#include "xattr-at.h"
#include "selinux-at.h"
static char const XATTRS_PREFIX[] = "SCHILY.xattr.";
enum { XATTRS_PREFIX_LEN = sizeof XATTRS_PREFIX - 1 };
void
xheader_xattr_init (struct tar_stat_info *st)
{
xattr_map_init (&st->xattr_map);
st->acls_a_ptr = NULL;
st->acls_a_len = 0;
st->acls_d_ptr = NULL;
st->acls_d_len = 0;
st->cntx_name = NULL;
}
void
xattr_map_init (struct xattr_map *map)
{
memset (map, 0, sizeof *map);
}
void
xattr_map_free (struct xattr_map *xattr_map)
{
for (idx_t i = 0; i < xattr_map->xm_size; i++)
{
free (xattr_map->xm_map[i].xkey);
free (xattr_map->xm_map[i].xval_ptr);
}
free (xattr_map->xm_map);
}
void
xattr_map_add (struct xattr_map *map,
const char *key, const char *val, idx_t len)
{
if (map->xm_size == map->xm_max)
map->xm_map = xpalloc (map->xm_map, &map->xm_max, 1, -1,
sizeof *map->xm_map);
struct xattr_array *p = &map->xm_map[map->xm_size];
p->xkey = xstrdup (key);
p->xval_ptr = ximemdup (val, len + 1);
p->xval_len = len;
map->xm_size++;
}
static void
xheader_xattr_add (struct tar_stat_info *st,
const char *key, const char *val, idx_t len)
{
idx_t klen = strlen (key);
char *xkey = xmalloc (XATTRS_PREFIX_LEN + klen + 1);
char *tmp = xkey;
tmp = stpcpy (tmp, XATTRS_PREFIX);
stpcpy (tmp, key);
xattr_map_add (&st->xattr_map, xkey, val, len);
free (xkey);
}
void
xattr_map_copy (struct xattr_map *dst, const struct xattr_map *src)
{
for (idx_t i = 0; i < src->xm_size; i++)
xattr_map_add (dst, src->xm_map[i].xkey,
src->xm_map[i].xval_ptr,
src->xm_map[i].xval_len);
}
struct xattrs_mask_map
{
const char **masks;
size_t size;
size_t used;
idx_t size;
idx_t used;
};
/* list of fnmatch patterns */
@@ -53,6 +124,10 @@ static struct
#ifdef HAVE_POSIX_ACLS
# include "acl.h"
# include <sys/acl.h>
# ifdef HAVE_ACL_LIBACL_H
# /* needed for numeric-owner support */
# include <acl/libacl.h>
# endif
#endif
#ifdef HAVE_POSIX_ACLS
@@ -179,7 +254,7 @@ fixup_extra_acl_fields (char *ptr)
while (*src)
{
const char *old = src;
size_t len = 0;
idx_t len = 0;
src = skip_to_ext_fields (src);
len = src - old;
@@ -203,14 +278,13 @@ fixup_extra_acl_fields (char *ptr)
attribute. Called only when acls_option > 0. */
static void
xattrs__acls_set (struct tar_stat_info const *st,
char const *file_name, int type,
char *ptr, size_t len, bool def)
char const *file_name, acl_type_t type,
char *ptr, bool def)
{
acl_t acl;
if (ptr)
{
/* assert (strlen (ptr) == len); */
ptr = fixup_extra_acl_fields (ptr);
acl = acl_from_text (ptr);
}
@@ -219,12 +293,11 @@ xattrs__acls_set (struct tar_stat_info const *st,
/* No "default" IEEE 1003.1e ACL set for directory. At this moment,
FILE_NAME may already have inherited default acls from parent
directory; clean them up. */
if (acl_delete_def_file_at (chdir_fd, file_name))
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
if (acl_delete_def_file_at (chdir_fd, file_name) < 0)
warnopt (WARN_XATTR_WRITE, errno,
_("acl_delete_def_file_at: Cannot drop default POSIX ACLs "
"for file '%s'"),
file_name));
file_name);
return;
}
else
@@ -236,12 +309,11 @@ xattrs__acls_set (struct tar_stat_info const *st,
return;
}
if (acl_set_file_at (chdir_fd, file_name, type, acl) == -1)
if (acl_set_file_at (chdir_fd, file_name, type, acl) < 0)
/* warn even if filesystem does not support acls */
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
file_name));
warnopt (WARN_XATTR_WRITE, errno,
_ ("acl_set_file_at: Cannot set POSIX ACLs for file '%s'"),
file_name);
acl_free (acl);
}
@@ -249,7 +321,7 @@ xattrs__acls_set (struct tar_stat_info const *st,
/* Cleanup textual representation of the ACL in VAL by eliminating tab
characters and comments */
static void
xattrs_acls_cleanup (char *val, size_t *plen)
xattrs_acls_cleanup (char *val, idx_t *plen)
{
char *p, *q;
@@ -271,21 +343,36 @@ xattrs_acls_cleanup (char *val, size_t *plen)
}
static void
xattrs__acls_get_a (int parentfd, const char *file_name,
struct tar_stat_info *st,
char **ret_ptr, size_t * ret_len)
acls_get_text (int parentfd, const char *file_name, acl_type_t type,
char **ret_ptr, idx_t *ret_len)
{
char *val = NULL;
acl_t acl;
if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_ACCESS)))
if (!(acl = acl_get_file_at (parentfd, file_name, type)))
{
if (errno != ENOTSUP)
call_arg_warn ("acl_get_file_at", file_name);
return;
}
val = acl_to_text (acl, NULL);
if (numeric_owner_option)
{
#ifdef HAVE_ACL_LIBACL_H
val = acl_to_any_text (acl, NULL, '\n',
TEXT_SOME_EFFECTIVE | TEXT_NUMERIC_IDS);
#else
static bool warned;
if (!warned)
{
warned = true;
paxwarn (0, _("--numeric-owner is ignored for ACLs:"
" libacl is not available"));
}
#endif
}
else
val = acl_to_text (acl, NULL);
acl_free (acl);
if (!val)
@@ -299,46 +386,31 @@ xattrs__acls_get_a (int parentfd, const char *file_name,
acl_free (val);
}
static void
xattrs__acls_get_a (int parentfd, const char *file_name,
char **ret_ptr, idx_t *ret_len)
{
acls_get_text (parentfd, file_name, ACL_TYPE_ACCESS, ret_ptr, ret_len);
}
/* "system.posix_acl_default" */
static void
xattrs__acls_get_d (int parentfd, char const *file_name,
struct tar_stat_info *st,
char **ret_ptr, size_t * ret_len)
char **ret_ptr, idx_t *ret_len)
{
char *val = NULL;
acl_t acl;
if (!(acl = acl_get_file_at (parentfd, file_name, ACL_TYPE_DEFAULT)))
{
if (errno != ENOTSUP)
call_arg_warn ("acl_get_file_at", file_name);
return;
}
val = acl_to_text (acl, NULL);
acl_free (acl);
if (!val)
{
call_arg_warn ("acl_to_text", file_name);
return;
}
*ret_ptr = xstrdup (val);
xattrs_acls_cleanup (*ret_ptr, ret_len);
acl_free (val);
acls_get_text (parentfd, file_name, ACL_TYPE_DEFAULT, ret_ptr, ret_len);
}
#endif /* HAVE_POSIX_ACLS */
static void
acls_one_line (const char *prefix, char delim,
const char *aclstring, size_t len)
const char *aclstring, idx_t len)
{
/* support both long and short text representation of posix acls */
struct obstack stk;
int pref_len = strlen (prefix);
idx_t pref_len = strlen (prefix);
const char *oldstring = aclstring;
int pos = 0;
idx_t pos = 0;
if (!aclstring || !len)
return;
@@ -346,7 +418,7 @@ acls_one_line (const char *prefix, char delim,
obstack_init (&stk);
while (pos <= len)
{
int move = strcspn (aclstring, ",\n");
idx_t move = strcspn (aclstring, ",\n");
if (!move)
break;
@@ -362,58 +434,63 @@ acls_one_line (const char *prefix, char delim,
obstack_1grow (&stk, '\0');
fprintf (stdlis, "%s", (char *) obstack_finish (&stk));
fputs (obstack_finish (&stk), stdlis);
obstack_free (&stk, NULL);
}
void
xattrs_acls_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd, int xisfile)
xattrs_acls_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED struct tar_stat_info *st,
MAYBE_UNUSED bool xisfile)
{
if (acls_option > 0)
{
#ifndef HAVE_POSIX_ACLS
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("POSIX ACL support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("POSIX ACL support is not available"));
}
#else
int err = file_has_acl_at (parentfd, file_name, &st->stat);
if (err == 0)
return;
if (err == -1)
if (err < 0)
{
call_arg_warn ("file_has_acl_at", file_name);
return;
}
xattrs__acls_get_a (parentfd, file_name, st,
xattrs__acls_get_a (parentfd, file_name,
&st->acls_a_ptr, &st->acls_a_len);
if (!xisfile)
xattrs__acls_get_d (parentfd, file_name, st,
xattrs__acls_get_d (parentfd, file_name,
&st->acls_d_ptr, &st->acls_d_len);
#endif
}
}
void
xattrs_acls_set (struct tar_stat_info const *st,
char const *file_name, char typeflag)
xattrs_acls_set (MAYBE_UNUSED struct tar_stat_info const *st,
MAYBE_UNUSED char const *file_name, char typeflag)
{
if (acls_option > 0 && typeflag != SYMTYPE)
{
#ifndef HAVE_POSIX_ACLS
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("POSIX ACL support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("POSIX ACL support is not available"));
}
#else
xattrs__acls_set (st, file_name, ACL_TYPE_ACCESS,
st->acls_a_ptr, st->acls_a_len, false);
st->acls_a_ptr, false);
if (typeflag == DIRTYPE || typeflag == GNUTYPE_DUMPDIR)
xattrs__acls_set (st, file_name, ACL_TYPE_DEFAULT,
st->acls_d_ptr, st->acls_d_len, true);
st->acls_d_ptr, true);
#endif
}
}
@@ -422,11 +499,7 @@ static void
mask_map_realloc (struct xattrs_mask_map *map)
{
if (map->used == map->size)
{
if (map->size == 0)
map->size = 4;
map->masks = x2nrealloc (map->masks, &map->size, sizeof (map->masks[0]));
}
map->masks = xpalloc (map->masks, &map->size, 1, -1, sizeof *map->masks);
}
void
@@ -441,20 +514,6 @@ xattrs_mask_add (const char *mask, bool incl)
mask_map->masks[mask_map->used++] = mask;
}
static void
clear_mask_map (struct xattrs_mask_map *mask_map)
{
if (mask_map->size)
free (mask_map->masks);
}
void
xattrs_clear_setup (void)
{
clear_mask_map (&xattrs_setup.incl);
clear_mask_map (&xattrs_setup.excl);
}
static bool xattrs_masked_out (const char *kw, bool archiving);
/* get xattrs from file given by FILE_NAME or FD (when non-zero)
@@ -465,56 +524,55 @@ void
xattrs_xattrs_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd)
{
if (xattrs_option > 0)
if (xattrs_option)
{
#ifndef HAVE_XATTRS
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("XATTR support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("XATTR support is not available"));
}
#else
static size_t xsz = 1024;
static idx_t xsz = 1024 / 2 * 3;
static char *xatrs = NULL;
ssize_t xret = -1;
ssize_t xret;
if (!xatrs)
xatrs = x2nrealloc (xatrs, &xsz, 1);
while (((fd == 0) ?
((xret =
llistxattrat (parentfd, file_name, xatrs, xsz)) == -1) :
((xret = flistxattr (fd, xatrs, xsz)) == -1))
&& (errno == ERANGE))
while (!xatrs
|| (((xret = (fd == 0
? listxattrat (parentfd, file_name, xatrs, xsz)
: flistxattr (fd, xatrs, xsz)))
< 0)
&& errno == ERANGE))
{
xatrs = x2nrealloc (xatrs, &xsz, 1);
xatrs = xpalloc (xatrs, &xsz, 1, -1, sizeof *xatrs);
}
if (xret == -1)
if (xret < 0)
call_arg_warn ((fd == 0) ? "llistxattrat" : "flistxattr", file_name);
else
{
const char *attr = xatrs;
static size_t asz = 1024;
static idx_t asz = 1024 / 2 * 3;
static char *val = NULL;
if (!val)
val = x2nrealloc (val, &asz, 1);
while (xret > 0)
{
size_t len = strlen (attr);
idx_t len = strlen (attr);
ssize_t aret = 0;
while (((fd == 0)
? ((aret = lgetxattrat (parentfd, file_name, attr,
val, asz)) == -1)
: ((aret = fgetxattr (fd, attr, val, asz)) == -1))
&& (errno == ERANGE))
while (!val
|| (((aret = (fd == 0
? lgetxattrat (parentfd, file_name, attr,
val, asz)
: fgetxattr (fd, attr, val, asz)))
< 0)
&& errno == ERANGE))
{
val = x2nrealloc (val, &asz, 1);
val = xpalloc (val, &asz, 1, -1, sizeof *val);
}
if (aret != -1)
if (0 <= aret)
{
if (!xattrs_masked_out (attr, true))
xheader_xattr_add (st, attr, val, aret);
@@ -533,14 +591,13 @@ xattrs_xattrs_get (int parentfd, char const *file_name,
#ifdef HAVE_XATTRS
static void
xattrs__fd_set (struct tar_stat_info const *st,
char const *file_name, char typeflag,
const char *attr, const char *ptr, size_t len)
xattrs__fd_set (char const *file_name, char typeflag,
const char *attr, const char *ptr, idx_t len)
{
if (ptr)
{
const char *sysname = "setxattrat";
int ret = -1;
int ret;
if (typeflag != SYMTYPE)
ret = setxattrat (chdir_fd, file_name, attr, ptr, len, 0);
@@ -550,11 +607,10 @@ xattrs__fd_set (struct tar_stat_info const *st,
ret = lsetxattrat (chdir_fd, file_name, attr, ptr, len, 0);
}
if (ret == -1)
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_("%s: Cannot set '%s' extended attribute for file '%s'"),
sysname, attr, file_name));
if (ret < 0)
warnopt (WARN_XATTR_WRITE, errno,
_("%s: Cannot set '%s' extended attribute for file '%s'"),
sysname, attr, file_name);
}
}
#endif
@@ -562,38 +618,42 @@ xattrs__fd_set (struct tar_stat_info const *st,
/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
zero, otherwise the fgetfileconat is used against correct file descriptor */
void
xattrs_selinux_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd)
xattrs_selinux_get (MAYBE_UNUSED int parentfd, MAYBE_UNUSED char const *file_name,
MAYBE_UNUSED struct tar_stat_info *st, MAYBE_UNUSED int fd)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("SELinux support is not available"));
}
#else
int result = fd ?
fgetfilecon (fd, &st->cntx_name)
: lgetfileconat (parentfd, file_name, &st->cntx_name);
int result = (fd
? fgetfilecon (fd, &st->cntx_name)
: lgetfileconat (parentfd, file_name, &st->cntx_name));
if (result == -1 && errno != ENODATA && errno != ENOTSUP)
if (result < 0 && errno != ENODATA && errno != ENOTSUP)
call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
#endif
}
}
void
xattrs_selinux_set (struct tar_stat_info const *st,
char const *file_name, char typeflag)
xattrs_selinux_set (MAYBE_UNUSED struct tar_stat_info const *st,
MAYBE_UNUSED char const *file_name, MAYBE_UNUSED char typeflag)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("SELinux support is not available"));
}
#else
const char *sysname = "setfilecon";
int ret;
@@ -612,11 +672,10 @@ xattrs_selinux_set (struct tar_stat_info const *st,
sysname = "lsetfileconat";
}
if (ret == -1)
WARNOPT (WARN_XATTR_WRITE,
(0, errno,
_("%s: Cannot set SELinux context for file '%s'"),
sysname, file_name));
if (ret < 0)
warnopt (WARN_XATTR_WRITE, errno,
_("%s: Cannot set SELinux context for file '%s'"),
sysname, file_name);
#endif
}
}
@@ -624,23 +683,20 @@ xattrs_selinux_set (struct tar_stat_info const *st,
static bool
xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
{
int i;
if (!mm->size)
return false;
for (i = 0; i < mm->used; i++)
for (idx_t i = 0; i < mm->used; i++)
if (fnmatch (mm->masks[i], kw, 0) == 0)
return true;
return false;
}
#define USER_DOT_PFX "user."
static bool
xattrs_kw_included (const char *kw, bool archiving)
{
static char const USER_DOT_PFX[] = "user.";
if (xattrs_setup.incl.size)
return xattrs_matches_mask (kw, &xattrs_setup.incl);
else if (archiving)
@@ -650,7 +706,7 @@ xattrs_kw_included (const char *kw, bool archiving)
}
static bool
xattrs_kw_excluded (const char *kw, bool archiving)
xattrs_kw_excluded (const char *kw)
{
return xattrs_setup.excl.size ?
xattrs_matches_mask (kw, &xattrs_setup.excl) : false;
@@ -662,31 +718,29 @@ xattrs_kw_excluded (const char *kw, bool archiving)
static bool
xattrs_masked_out (const char *kw, bool archiving)
{
return xattrs_kw_included (kw, archiving) ?
xattrs_kw_excluded (kw, archiving) : true;
return xattrs_kw_included (kw, archiving) ? xattrs_kw_excluded (kw) : true;
}
void
xattrs_xattrs_set (struct tar_stat_info const *st,
char const *file_name, char typeflag, int later_run)
char const *file_name, char typeflag, bool later_run)
{
if (xattrs_option > 0)
if (xattrs_option)
{
#ifndef HAVE_XATTRS
static int done = 0;
static bool done;
if (!done)
WARN ((0, 0, _("XATTR support is not available")));
done = 1;
{
done = true;
paxwarn (0, _("XATTR support is not available"));
}
#else
size_t scan = 0;
if (!st->xattr_map_size)
if (!st->xattr_map.xm_size)
return;
for (; scan < st->xattr_map_size; ++scan)
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
{
char *keyword = st->xattr_map[scan].xkey;
keyword += strlen ("SCHILY.xattr.");
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
/* TODO: this 'later_run' workaround is temporary solution -> once
capabilities should become fully supported by it's API and there
@@ -695,16 +749,16 @@ xattrs_xattrs_set (struct tar_stat_info const *st,
the first run except 'security.capability' which is restored in
'later_run == 1'. */
if (typeflag == REGTYPE
&& later_run == !!strcmp (keyword, "security.capability"))
&& later_run == (strcmp (keyword, "security.capability") != 0))
continue;
if (xattrs_masked_out (keyword, false /* extracting */ ))
/* we don't want to restore this keyword */
continue;
xattrs__fd_set (st, file_name, typeflag, keyword,
st->xattr_map[scan].xval_ptr,
st->xattr_map[scan].xval_len);
xattrs__fd_set (file_name, typeflag, keyword,
st->xattr_map.xm_map[i].xval_ptr,
st->xattr_map.xm_map[i].xval_len);
}
#endif
}
@@ -713,25 +767,23 @@ xattrs_xattrs_set (struct tar_stat_info const *st,
void
xattrs_print_char (struct tar_stat_info const *st, char *output)
{
int i;
if (verbose_option < 2)
{
*output = 0;
return;
}
if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
if (xattrs_option || selinux_context_option > 0 || acls_option > 0)
{
/* placeholders */
*output = ' ';
output[1] = 0;
}
if (xattrs_option > 0 && st->xattr_map_size)
for (i = 0; i < st->xattr_map_size; ++i)
if (xattrs_option && st->xattr_map.xm_size)
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
{
char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
if (!xattrs_masked_out (keyword, false /* like extracting */ ))
{
*output = '*';
@@ -768,16 +820,14 @@ xattrs_print (struct tar_stat_info const *st)
}
/* xattrs */
if (xattrs_option > 0 && st->xattr_map_size)
if (xattrs_option && st->xattr_map.xm_size)
{
int i;
for (i = 0; i < st->xattr_map_size; ++i)
for (idx_t i = 0; i < st->xattr_map.xm_size; i++)
{
char *keyword = st->xattr_map[i].xkey + strlen ("SCHILY.xattr.");
char *keyword = st->xattr_map.xm_map[i].xkey + XATTRS_PREFIX_LEN;
if (!xattrs_masked_out (keyword, false /* like extracting */ ))
fprintf (stdlis, " x: %lu %s\n",
(unsigned long) st->xattr_map[i].xval_len, keyword);
fprintf (stdlis, " x: %td %s\n",
st->xattr_map.xm_map[i].xval_len, keyword);
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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