diff --git a/Avoid-EOVERFLOW-problems-in-some-symlink-tests.patch b/Avoid-EOVERFLOW-problems-in-some-symlink-tests.patch new file mode 100644 index 0000000000000000000000000000000000000000..8395f894e95477cf59c3b2b1375da1c1cb271b13 --- /dev/null +++ b/Avoid-EOVERFLOW-problems-in-some-symlink-tests.patch @@ -0,0 +1,76 @@ +From d935dc7d1c150b3425dd43dc13a4dd2e2b712c26 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Mon, 13 Jun 2022 17:02:54 -0700 +Subject: [PATCH] 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. +--- + src/extract.c | 28 ++++++++++++---------------- + 1 file changed, 12 insertions(+), 16 deletions(-) + +diff --git a/src/extract.c b/src/extract.c +index fda4617d..6d2543f0 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -982,18 +982,12 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) + + + static bool +-is_directory_link (const char *file_name) ++is_directory_link (char const *file_name, struct stat *st) + { +- struct stat st; +- int e = errno; +- int res; +- +- res = (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 && +- S_ISLNK (st.st_mode) && +- fstatat (chdir_fd, file_name, &st, 0) == 0 && +- S_ISDIR (st.st_mode)); +- errno = e; +- return res; ++ char buf[1]; ++ return (0 <= readlinkat (chdir_fd, file_name, buf, sizeof buf) ++ && fstatat (chdir_fd, file_name, st, 0) == 0 ++ && S_ISDIR (st->st_mode)); + } + + /* Given struct stat of a directory (or directory member) whose ownership +@@ -1066,11 +1060,14 @@ extract_dir (char *file_name, int typeflag) + || old_files_option == OVERWRITE_OLD_FILES)) + { + struct stat st; ++ st.st_mode = 0; + +- if (keep_directory_symlink_option && is_directory_link (file_name)) ++ if (keep_directory_symlink_option ++ && is_directory_link (file_name, &st)) + return 0; + +- if (deref_stat (file_name, &st) == 0) ++ if ((st.st_mode != 0 && fstatat_flags == 0) ++ || deref_stat (file_name, &st) == 0) + { + current_mode = st.st_mode; + current_mode_mask = ALL_MODE_BITS; +@@ -1178,9 +1175,8 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, + if (! HAVE_WORKING_O_NOFOLLOW + && overwriting_old_files && ! dereference_option) + { +- struct stat st; +- if (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 +- && S_ISLNK (st.st_mode)) ++ char buf[1]; ++ if (0 <= readlinkat (chdir_fd, file_name, buf, sizeof buf)) + { + errno = ELOOP; + return -1; +-- +2.31.1 + diff --git a/Avoid-excess-lseek-etc.patch b/Avoid-excess-lseek-etc.patch new file mode 100644 index 0000000000000000000000000000000000000000..b094515e74a61ae3352fafc2c7ad1a41d3fa8402 --- /dev/null +++ b/Avoid-excess-lseek-etc.patch @@ -0,0 +1,504 @@ +From 66be5a789e70f911170017754fc51e499998f90e Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sun, 14 Aug 2022 16:32:26 -0700 +Subject: [PATCH] Avoid excess lseek etc. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* 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. +--- + src/buffer.c | 99 ++++++++++++++++++--------------------------------- + src/common.h | 10 +++--- + src/compare.c | 31 ++++------------ + src/delete.c | 51 +++++++++----------------- + src/system.c | 81 +++++++++++++++++++++++++++-------------- + 5 files changed, 119 insertions(+), 153 deletions(-) + +diff --git a/src/buffer.c b/src/buffer.c +index a4467aa2..36b9c9c0 100644 +--- a/src/buffer.c ++++ b/src/buffer.c +@@ -20,7 +20,6 @@ + Written by John Gilmore, on 1985-08-25. */ + + #include +-#include + + #include + +@@ -421,37 +420,6 @@ check_compressed_archive (bool *pshort) + return ct_none; + } + +-/* Guess if the archive is seekable. */ +-static void +-guess_seekable_archive (void) +-{ +- struct stat st; +- +- if (subcommand_option == DELETE_SUBCOMMAND) +- { +- /* The current code in delete.c is based on the assumption that +- skip_member() reads all data from the archive. So, we should +- make sure it won't use seeks. On the other hand, the same code +- depends on the ability to backspace a record in the archive, +- so setting seekable_archive to false is technically incorrect. +- However, it is tested only in skip_member(), so it's not a +- problem. */ +- seekable_archive = false; +- } +- +- if (seek_option != -1) +- { +- seekable_archive = !!seek_option; +- return; +- } +- +- if (!multi_volume_option && !use_compress_program_option +- && fstat (archive, &st) == 0) +- seekable_archive = S_ISREG (st.st_mode); +- else +- seekable_archive = false; +-} +- + /* Open an archive named archive_name_array[0]. Detect if it is + a compressed archive of known type and use corresponding decompression + program if so */ +@@ -703,12 +671,41 @@ check_tty (enum access_mode mode) + } + } + ++/* Fetch the status of the archive, accessed via WANTED_STATUS. */ ++ ++static void ++get_archive_status (enum access_mode wanted_access, bool backed_up_flag) ++{ ++ if (!sys_get_archive_stat ()) ++ { ++ int saved_errno = errno; ++ ++ if (backed_up_flag) ++ undo_last_backup (); ++ errno = saved_errno; ++ open_fatal (archive_name_array[0]); ++ } ++ ++ seekable_archive ++ = (! (multi_volume_option || use_compress_program_option) ++ && (seek_option < 0 ++ ? (_isrmt (archive) ++ || S_ISREG (archive_stat.st_mode) ++ || S_ISBLK (archive_stat.st_mode)) ++ : seek_option)); ++ ++ if (wanted_access != ACCESS_READ) ++ sys_detect_dev_null_output (); ++ ++ SET_BINARY_MODE (archive); ++} ++ + /* Open an archive file. The argument specifies whether we are + reading or writing, or both. */ + static void + _open_archive (enum access_mode wanted_access) + { +- int backed_up_flag = 0; ++ bool backed_up_flag = false; + + if (record_size == 0) + FATAL_ERROR ((0, 0, _("Invalid value for record_size"))); +@@ -797,15 +794,13 @@ _open_archive (enum access_mode wanted_access) + { + case ACCESS_READ: + archive = open_compressed_archive (); +- if (archive >= 0) +- guess_seekable_archive (); + break; + + case ACCESS_WRITE: + if (backup_option) + { + maybe_backup_file (archive_name_array[0], 1); +- backed_up_flag = 1; ++ backed_up_flag = true; + } + if (verify_option) + archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY, +@@ -833,20 +828,7 @@ _open_archive (enum access_mode wanted_access) + break; + } + +- if (archive < 0 +- || (! _isrmt (archive) && !sys_get_archive_stat ())) +- { +- int saved_errno = errno; +- +- if (backed_up_flag) +- undo_last_backup (); +- errno = saved_errno; +- open_fatal (archive_name_array[0]); +- } +- +- sys_detect_dev_null_output (); +- sys_save_archive_dev_ino (); +- SET_BINARY_MODE (archive); ++ get_archive_status (wanted_access, backed_up_flag); + + switch (wanted_access) + { +@@ -1049,18 +1031,8 @@ flush_archive (void) + static void + backspace_output (void) + { +-#ifdef MTIOCTOP +- { +- struct mtop operation; +- +- operation.mt_op = MTBSR; +- operation.mt_count = 1; +- if (rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0) +- return; +- if (errno == EIO && rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0) +- return; +- } +-#endif ++ if (mtioseek (false, -1)) ++ return; + + { + off_t position = rmtlseek (archive, (off_t) 0, SEEK_CUR); +@@ -1373,7 +1345,6 @@ new_volume (enum access_mode mode) + case ACCESS_READ: + archive = rmtopen (*archive_name_cursor, O_RDONLY, MODE_RW, + rsh_command_option); +- guess_seekable_archive (); + break; + + case ACCESS_WRITE: +@@ -1398,7 +1369,7 @@ new_volume (enum access_mode mode) + goto tryagain; + } + +- SET_BINARY_MODE (archive); ++ get_archive_status (mode, false); + + return true; + } +diff --git a/src/common.h b/src/common.h +index 62149113..58c1b6c1 100644 +--- a/src/common.h ++++ b/src/common.h +@@ -392,9 +392,8 @@ struct name + char *caname; /* canonical name */ + }; + +-/* Obnoxious test to see if dimwit is trying to dump the archive. */ +-GLOBAL dev_t ar_dev; +-GLOBAL ino_t ar_ino; ++/* Status of archive file, or all zeros if remote. */ ++GLOBAL struct stat archive_stat; + + /* Flags for reading, searching, and fstatatting files. */ + GLOBAL int open_read_flags; +@@ -402,6 +401,9 @@ GLOBAL int open_searchdir_flags; + GLOBAL int fstatat_flags; + + GLOBAL int seek_option; ++ ++/* true if archive if lseek should be used on the archive, 0 if it ++ should not be used. */ + GLOBAL bool seekable_archive; + + GLOBAL dev_t root_device; +@@ -896,7 +898,6 @@ void xattr_map_free (struct xattr_map *xattr_map); + /* Module system.c */ + + void sys_detect_dev_null_output (void); +-void sys_save_archive_dev_ino (void); + void sys_wait_for_child (pid_t, bool); + void sys_spawn_shell (void); + bool sys_compare_uid (struct stat *a, struct stat *b); +@@ -914,6 +915,7 @@ int 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); ++bool mtioseek (bool count_files, off_t count); + + /* Module compare.c */ + void report_difference (struct tar_stat_info *st, const char *message, ...) +diff --git a/src/compare.c b/src/compare.c +index 752eb15a..90043765 100644 +--- a/src/compare.c ++++ b/src/compare.c +@@ -565,31 +565,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; +diff --git a/src/delete.c b/src/delete.c +index fdf18af9..1d4d2681 100644 +--- a/src/delete.c ++++ b/src/delete.c +@@ -18,7 +18,6 @@ + along with this program. If not, see . */ + + #include +-#include + + #include "common.h" + #include +@@ -50,41 +49,25 @@ 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 (mtioseek (false, count)) ++ return; + +- if (errno == EIO +- && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation)) ++ off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0; ++ if (0 <= position0) ++ { ++ off_t increment; ++ if (INT_MULTIPLY_WRAPV (record_size, count, &increment) ++ || INT_ADD_WRAPV (position0, increment, &position) ++ || position < 0) ++ { ++ ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0])); + 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); +- +- 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, +diff --git a/src/system.c b/src/system.c +index 1f560783..9f8bbe4c 100644 +--- a/src/system.c ++++ b/src/system.c +@@ -16,6 +16,7 @@ + with this program. If not, see . */ + + #include ++#include + + #include "common.h" + #include +@@ -37,6 +38,35 @@ xexec (const char *cmd) + exec_fatal (cmd); + } + ++/* 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 ++ ? INT_SUBTRACT_WRAPV (0, count, &operation.mt_count) ++ : INT_ADD_WRAPV (count, 0, &operation.mt_count)) ++ && (0 <= rmtioctl (archive, MTIOCTOP, &operation) ++ || (errno == EIO ++ && 0 <= rmtioctl (archive, MTIOCTOP, &operation)))) ++ return true; ++#endif ++ ++ mtioseekable_archive = false; ++ } ++ return false; ++} ++ + #if MSDOS + + bool +@@ -51,11 +81,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) + { +@@ -128,31 +153,34 @@ sys_child_open_for_uncompress (void) + + 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); +-} +- +-/* 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; ++ return (!dev_null_output && !_isrmt (archive) ++ && p->stat.st_dev == archive_stat.st_dev ++ && p->stat.st_ino == archive_stat.st_ino); + } + + /* Detect if outputting to "/dev/null". */ +@@ -160,14 +188,15 @@ 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) ++ && archive_stat.st_ino == dev_null_stat.st_ino ++ && archive_stat.st_dev == dev_null_stat.st_dev)); + } + + void +-- +2.31.1 + diff --git a/Fix-Af-F-bug-when-F-is-not-a-regular-file.patch b/Fix-Af-F-bug-when-F-is-not-a-regular-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..e07613df22943cd4f0125f6c5debdabebab60a10 --- /dev/null +++ b/Fix-Af-F-bug-when-F-is-not-a-regular-file.patch @@ -0,0 +1,88 @@ +From 24c830696527566742f1c8cac70d53086afca599 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 3 Nov 2022 23:07:11 -0700 +Subject: [PATCH 1/9] Fix -Af F bug when F is not a regular file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. +--- + src/update.c | 49 ++++++++++++------------------------------------- + 1 file changed, 12 insertions(+), 37 deletions(-) + +diff --git a/src/update.c b/src/update.c +index 5424e2ce..44ab229d 100644 +--- a/src/update.c ++++ b/src/update.c +@@ -50,7 +50,6 @@ 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) + { +@@ -58,43 +57,19 @@ 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 (); ++ size_t status = safe_read (handle, start->buffer, ++ available_space_after (start)); ++ if (status == 0) ++ break; ++ if (status == SAFE_READ_ERROR) ++ read_fatal (file_name); ++ if (status % BLOCKSIZE) ++ memset (start->buffer + status - status % BLOCKSIZE, 0, ++ BLOCKSIZE - status % BLOCKSIZE); ++ set_next_block_after (start + (status - 1) / BLOCKSIZE); + } + + if (close (handle) != 0) +-- +2.31.1 + diff --git a/Fix-assume-include.patch b/Fix-assume-include.patch new file mode 100644 index 0000000000000000000000000000000000000000..f265107a4fe56616cc751aa78f31a1e9fe3eaf78 --- /dev/null +++ b/Fix-assume-include.patch @@ -0,0 +1,28 @@ +From 71d1619abd8de3d67105230a301264d0de86c0c7 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 6 Jan 2023 12:47:09 -0800 +Subject: [PATCH 6/9] =?UTF-8?q?Fix=20=E2=80=98assume=E2=80=99=20include?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* src/tar.c: Include verify.h, needed for ‘assume’. +--- + src/tar.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/tar.c b/src/tar.c +index 0494e236..d65fc806 100644 +--- a/src/tar.c ++++ b/src/tar.c +@@ -47,6 +47,7 @@ + #include + #include + #include ++#include + #include + #include + #include +-- +2.31.1 + diff --git a/Fix-bug-with-x-xattr-read-only-files.patch b/Fix-bug-with-x-xattr-read-only-files.patch new file mode 100644 index 0000000000000000000000000000000000000000..bb314a266a2b652fc0a7292b5a6872bb08dd6a88 --- /dev/null +++ b/Fix-bug-with-x-xattr-read-only-files.patch @@ -0,0 +1,163 @@ +From 0b74885e81b90d6ab4890b195dce99ca9109fe59 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 26 Aug 2022 15:23:23 -0500 +Subject: [PATCH] Fix bug with -x --xattr read-only files +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. +--- + src/extract.c | 31 +++++++++++++++---------------- + tests/Makefile.am | 1 + + tests/testsuite.at | 1 + + tests/xattr08.at | 41 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 58 insertions(+), 16 deletions(-) + create mode 100644 tests/xattr08.at + +diff --git a/src/extract.c b/src/extract.c +index 2813c961..c1f5731b 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -1189,14 +1189,12 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, + int fd; + bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES; + int openflag = (O_WRONLY | O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK +- | O_CREAT +- | (overwriting_old_files +- ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW) +- : O_EXCL)); +- +- /* File might be created in set_xattr. So clear O_EXCL to avoid open() fail */ +- if (file_created) +- openflag = openflag & ~O_EXCL; ++ | (file_created ++ ? O_NOFOLLOW ++ : (O_CREAT ++ | (overwriting_old_files ++ ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW) ++ : O_EXCL)))); + + if (typeflag == CONTTYPE) + { +@@ -1227,7 +1225,12 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, + fd = openat (chdir_fd, file_name, openflag, mode); + if (0 <= fd) + { +- if (overwriting_old_files) ++ if (openflag & O_EXCL) ++ { ++ *current_mode = mode & ~ current_umask; ++ *current_mode_mask = MODE_RWX; ++ } ++ else + { + struct stat st; + if (fstat (fd, &st) != 0) +@@ -1246,11 +1249,6 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, + *current_mode = st.st_mode; + *current_mode_mask = ALL_MODE_BITS; + } +- else +- { +- *current_mode = mode & ~ current_umask; +- *current_mode_mask = MODE_RWX; +- } + } + + return fd; +@@ -1268,8 +1266,9 @@ extract_file (char *file_name, int typeflag) + bool interdir_made = false; + mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX + & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0)); +- mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) +- : 0; ++ mode_t invert_permissions ++ = ((0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0) ++ | ((mode & S_IWUSR) ^ S_IWUSR)); + mode_t current_mode = 0; + mode_t current_mode_mask = 0; + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index eff8a3bf..a0ce690b 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -275,6 +275,7 @@ TESTSUITE_AT = \ + xattr05.at\ + xattr06.at\ + xattr07.at\ ++ xattr08.at\ + acls01.at\ + acls02.at\ + acls03.at\ +diff --git a/tests/testsuite.at b/tests/testsuite.at +index 0769e71b..a99cdeee 100644 +--- a/tests/testsuite.at ++++ b/tests/testsuite.at +@@ -502,6 +502,7 @@ m4_include([xattr04.at]) + m4_include([xattr05.at]) + m4_include([xattr06.at]) + m4_include([xattr07.at]) ++m4_include([xattr08.at]) + + m4_include([acls01.at]) + m4_include([acls02.at]) +diff --git a/tests/xattr08.at b/tests/xattr08.at +new file mode 100644 +index 00000000..2beef235 +--- /dev/null ++++ b/tests/xattr08.at +@@ -0,0 +1,41 @@ ++# Process this file with autom4te to create testsuite. -*- Autotest -*- ++# ++# Test suite for GNU tar. ++# Copyright 2022 Free Software Foundation, Inc. ++ ++# This file is part of GNU tar. ++ ++# 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 ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++ ++# GNU tar 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 . ++# ++# Test description: ++# Test read-only files can be extracted with --xattr. ++# Per report: ++# https://lists.gnu.org/r/bug-tar/2020-10/msg00001.html ++ ++AT_SETUP([xattrs: xattrs and read-only files]) ++AT_KEYWORDS([xattrs xattr08]) ++ ++AT_TAR_CHECK([ ++AT_XATTRS_PREREQ ++mkdir dir dir2 ++genfile --file dir/file ++ ++setfattr -n user.test -v OurDirValue dir/file ++chmod a-w dir/file ++ ++tar --xattrs -C dir -cf archive.tar file ++tar --xattrs -C dir2 -xf archive.tar ++]) ++ ++AT_CLEANUP +-- +2.31.1 + diff --git a/Fix-crash-on-invalid-command-line-argument.patch b/Fix-crash-on-invalid-command-line-argument.patch new file mode 100644 index 0000000000000000000000000000000000000000..78183ed3c102a3ccdbd0b3f6b6df989c7bac19c0 --- /dev/null +++ b/Fix-crash-on-invalid-command-line-argument.patch @@ -0,0 +1,32 @@ +From 93082d6eb84a086b02c7a8607f2553d5d9d0a93c Mon Sep 17 00:00:00 2001 +From: Samanta Navarro +Date: Fri, 11 Jun 2021 11:52:31 +0000 +Subject: [PATCH] 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=\" +--- + src/checkpoint.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/checkpoint.c b/src/checkpoint.c +index eb12c1c0..9d4895cc 100644 +--- a/src/checkpoint.c ++++ b/src/checkpoint.c +@@ -84,7 +84,7 @@ copy_string_unquote (const char *str) + char *output = xstrdup (str); + size_t len = strlen (output); + if ((*output == '"' || *output == '\'') +- && output[len-1] == *output) ++ && len > 1 && output[len-1] == *output) + { + memmove (output, output+1, len-2); + output[len-2] = 0; +-- +2.31.1 + diff --git a/Fix-data-loss-when-acting-as-filter.patch b/Fix-data-loss-when-acting-as-filter.patch new file mode 100644 index 0000000000000000000000000000000000000000..a5a5a09c60598845866d3a609bbf416e79156ef5 --- /dev/null +++ b/Fix-data-loss-when-acting-as-filter.patch @@ -0,0 +1,247 @@ +From bc277c7069cdebb6a6140326636555267b142e0e Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 3 Sep 2022 18:22:34 -0500 +Subject: [PATCH] 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. +--- + src/common.h | 5 +++-- + src/delete.c | 4 ++-- + src/extract.c | 2 +- + src/list.c | 22 ++++++++++++++++------ + src/sparse.c | 6 +++--- + src/update.c | 5 ++++- + tests/delete01.at | 4 +++- + tests/delete02.at | 4 +++- + 8 files changed, 35 insertions(+), 17 deletions(-) + +diff --git a/src/common.h b/src/common.h +index 24166524..259655f9 100644 +--- a/src/common.h ++++ b/src/common.h +@@ -623,8 +623,9 @@ enum read_header read_header (union block **return_block, + struct tar_stat_info *info, + enum read_header_mode m); + enum read_header tar_checksum (union block *header, bool silent); +-void skip_file (off_t size); ++void skim_file (off_t size, bool must_copy); + void skip_member (void); ++void skim_member (bool must_copy); + + /* Module misc.c. */ + +@@ -928,7 +929,7 @@ bool sparse_fixup_header (struct tar_stat_info *st); + enum dump_status sparse_dump_file (int, struct tar_stat_info *st); + enum dump_status sparse_extract_file (int fd, struct tar_stat_info *st, + off_t *size); +-enum dump_status sparse_skip_file (struct tar_stat_info *st); ++enum dump_status sparse_skim_file (struct tar_stat_info *st, bool must_copy); + bool sparse_diff_file (int, struct tar_stat_info *st); + + /* Module utf8.c */ +diff --git a/src/delete.c b/src/delete.c +index 3bee5c65..dd6bc213 100644 +--- a/src/delete.c ++++ b/src/delete.c +@@ -183,13 +183,13 @@ delete_archive_members (void) + case HEADER_SUCCESS: + if ((name = name_scan (current_stat_info.file_name)) == NULL) + { +- skip_member (); ++ skim_member (acting_as_filter); + break; + } + name->found_count++; + if (!ISFOUND (name)) + { +- skip_member (); ++ skim_member (acting_as_filter); + break; + } + FALLTHROUGH; +diff --git a/src/extract.c b/src/extract.c +index 7696d5e4..78de47f5 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -1320,7 +1320,7 @@ extract_file (char *file_name, int typeflag) + } + } + +- skip_file (size); ++ skim_file (size, false); + + mv_end (); + +diff --git a/src/list.c b/src/list.c +index 409917d2..cbc0e31e 100644 +--- a/src/list.c ++++ b/src/list.c +@@ -416,7 +416,7 @@ read_header (union block **return_block, struct tar_stat_info *info, + size_t next_long_name_blocks = 0; + size_t next_long_link_blocks = 0; + enum read_header status = HEADER_SUCCESS; +- ++ + while (1) + { + header = find_next_block (); +@@ -1391,15 +1391,17 @@ print_for_mkdir (char *dirname, int length, mode_t mode) + } + } + +-/* Skip over SIZE bytes of data in blocks in the archive. */ ++/* Skip over SIZE bytes of data in blocks in the archive. ++ This may involve copying the data. ++ If MUST_COPY, always copy instead of skipping. */ + void +-skip_file (off_t size) ++skim_file (off_t size, bool must_copy) + { + union block *x; + + /* FIXME: Make sure mv_begin_read is always called before it */ + +- if (seekable_archive) ++ if (seekable_archive && !must_copy) + { + off_t nblk = seek_archive (size); + if (nblk >= 0) +@@ -1426,6 +1428,14 @@ skip_file (off_t size) + NOTE: Current header must be decoded before calling this function. */ + void + skip_member (void) ++{ ++ skim_member (false); ++} ++ ++/* Skip the current member in the archive. ++ If MUST_COPY, always copy instead of skipping. */ ++void ++skim_member (bool must_copy) + { + if (!current_stat_info.skipped) + { +@@ -1435,9 +1445,9 @@ skip_member (void) + mv_begin_read (¤t_stat_info); + + if (current_stat_info.is_sparse) +- sparse_skip_file (¤t_stat_info); ++ sparse_skim_file (¤t_stat_info, must_copy); + else if (save_typeflag != DIRTYPE) +- skip_file (current_stat_info.stat.st_size); ++ skim_file (current_stat_info.stat.st_size, must_copy); + + mv_end (); + } +diff --git a/src/sparse.c b/src/sparse.c +index 0b9f250f..767793b0 100644 +--- a/src/sparse.c ++++ b/src/sparse.c +@@ -586,7 +586,7 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size) + } + + 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; +@@ -598,7 +598,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; + } + +@@ -721,7 +721,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); +diff --git a/src/update.c b/src/update.c +index eece0365..5424e2ce 100644 +--- a/src/update.c ++++ b/src/update.c +@@ -42,6 +42,8 @@ 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 +@@ -110,6 +112,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) +@@ -166,7 +169,7 @@ update_archive (void) + } + } + +- skip_member (); ++ skim_member (acting_as_filter); + break; + } + +diff --git a/tests/delete01.at b/tests/delete01.at +index f323975e..251b5497 100644 +--- a/tests/delete01.at ++++ b/tests/delete01.at +@@ -27,8 +27,10 @@ AT_TAR_CHECK([ + genfile -l 50000 --file file1 + genfile -l 1024 --file file2 + tar cf archive file1 file2 ++tar -f - --delete file2 archout ++tar tf archout + tar f archive --delete file2 +-tar tf archive], ++cmp archive archout], + [0], + [file1 + ]) +diff --git a/tests/delete02.at b/tests/delete02.at +index 694c85d7..a21cd6d4 100644 +--- a/tests/delete02.at ++++ b/tests/delete02.at +@@ -31,7 +31,9 @@ tar cf archive 1 2 3 + tar tf archive + cat archive | tar f - --delete 2 > archive2 + echo separator +-tar tf archive2], ++tar tf archive2 ++tar f - --delete 2 < archive > archive3 ++cmp archive2 archive3], + [0], + [1 + 2 +-- +2.31.1 + diff --git a/Fix-delete-bug-with-short-reads.patch b/Fix-delete-bug-with-short-reads.patch new file mode 100644 index 0000000000000000000000000000000000000000..8fae165f13ddb65cf325f88c360f7151f8a94571 --- /dev/null +++ b/Fix-delete-bug-with-short-reads.patch @@ -0,0 +1,53 @@ +From f8e14746d2ca72804a4520c059d7bf65ca00c5ac Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 2 Sep 2022 16:32:27 -0500 +Subject: [PATCH] Fix --delete bug with short reads +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* gnulib.modules: Add idx. +* src/common.h: Include idx.h. +* src/delete.c (move_archive): Don’t botch short reads. +--- + src/common.h | 1 + + src/delete.c | 10 ++++++++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/common.h b/src/common.h +index 58c1b6c1..24166524 100644 +--- a/src/common.h ++++ b/src/common.h +@@ -53,6 +53,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/delete.c b/src/delete.c +index 1d4d2681..3bee5c65 100644 +--- a/src/delete.c ++++ b/src/delete.c +@@ -55,9 +55,15 @@ move_archive (off_t count) + off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0; + if (0 <= position0) + { +- off_t increment; ++ /* 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 (INT_MULTIPLY_WRAPV (record_size, count, &increment) +- || INT_ADD_WRAPV (position0, increment, &position) ++ || INT_ADD_WRAPV (position0, start_offset, &move_start) ++ || INT_ADD_WRAPV (move_start, increment, &position) + || position < 0) + { + ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0])); +-- +2.31.1 + diff --git a/Fix-handling-of-extended-header-prefixes.patch b/Fix-handling-of-extended-header-prefixes.patch new file mode 100644 index 0000000000000000000000000000000000000000..18500792de6fea51c67a9fc4978e19f591faf26d --- /dev/null +++ b/Fix-handling-of-extended-header-prefixes.patch @@ -0,0 +1,60 @@ +From a339f05cd269013fa133d2f148d73f6f7d4247e4 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sat, 28 Aug 2021 16:02:12 +0300 +Subject: [PATCH] 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 +--- + src/xheader.c | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +diff --git a/src/xheader.c b/src/xheader.c +index 4f8b2b27..3cd694d1 100644 +--- a/src/xheader.c ++++ b/src/xheader.c +@@ -637,11 +637,11 @@ static struct xhdr_tab const * + locate_handler (char const *keyword) + { + struct xhdr_tab const *p; +- + for (p = xhdr_tab; p->keyword; p++) + if (p->prefix) + { +- if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0) ++ size_t kwlen = strlen (p->keyword); ++ if (keyword[kwlen] == '.' && strncmp (p->keyword, keyword, kwlen) == 0) + return p; + } + else +@@ -1716,19 +1716,20 @@ xattr_decoder (struct tar_stat_info *st, + char const *keyword, char const *arg, size_t size) + { + char *xstr, *xkey; +- ++ + /* copy keyword */ +- size_t klen_raw = strlen (keyword); +- xkey = alloca (klen_raw + 1); +- memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */; ++ xkey = xstrdup (keyword); + + /* copy value */ +- xstr = alloca (size + 1); ++ xstr = xmalloc (size + 1); + memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */; + + xattr_decode_keyword (xkey); + +- xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size); ++ xheader_xattr_add (st, xkey + strlen ("SCHILY.xattr."), xstr, size); ++ ++ free (xkey); ++ free (xstr); + } + + static void +-- +2.31.1 + diff --git a/Fix-null-rereference-if-low-memory.patch b/Fix-null-rereference-if-low-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..5b0b7671787d8b70045aceb67ab0b11f05fd5849 --- /dev/null +++ b/Fix-null-rereference-if-low-memory.patch @@ -0,0 +1,51 @@ +From 7958eb97e6ca2aca66c2d5a45b7e803f31646e4e Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Fri, 17 Sep 2021 12:36:13 -0700 +Subject: [PATCH] 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. +--- + src/incremen.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/incremen.c b/src/incremen.c +index 4f909602..c05504be 100644 +--- a/src/incremen.c ++++ b/src/incremen.c +@@ -903,19 +903,19 @@ store_rename (struct directory *dir, struct obstack *stk) + else + { + char *temp_name; +- ++ + /* Break the cycle by using a temporary name for one of its + elements. + First, create a temp name stub entry. */ + temp_name = dir_name (dir->name); + obstack_1grow (stk, 'X'); + obstack_grow (stk, temp_name, strlen (temp_name) + 1); +- ++ + obstack_code_rename (stk, dir->name, ""); +- ++ + for (p = dir; p != prev; p = p->orig) + obstack_code_rename (stk, p->orig->name, p->name); +- ++ + obstack_code_rename (stk, "", prev->name); + free (temp_name); + } +@@ -993,7 +993,7 @@ read_incr_db_01 (int version, const char *initbuf) + } + else + { +- buf = strdup (initbuf); ++ buf = xstrdup (initbuf); + bufsize = strlen (buf) + 1; + } + +-- +2.31.1 + diff --git a/Fix-savannah-bug-63123.patch b/Fix-savannah-bug-63123.patch new file mode 100644 index 0000000000000000000000000000000000000000..98c75dba3fc3f44cabb5c7cfe162536ceb8c4890 --- /dev/null +++ b/Fix-savannah-bug-63123.patch @@ -0,0 +1,187 @@ +From 17debecd7300e94f590b8ce167a8c0735cb6d57d Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sat, 22 Oct 2022 12:06:45 +0300 +Subject: [PATCH] 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. +--- + src/extract.c | 108 +++++++++++++++++++++++++++----------------------- + 1 file changed, 58 insertions(+), 50 deletions(-) + +diff --git a/src/extract.c b/src/extract.c +index 78de47f5..37ab2956 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -679,9 +679,10 @@ fixup_delayed_set_stat (char const *src, char const *dst) + + /* After a file/link/directory creation has failed due to ENOENT, + create all required directories. Return zero if all the required +- directories were created, nonzero (issuing a diagnostic) otherwise. */ ++ directories were created, nonzero (issuing a diagnostic) otherwise. ++ Set *INTERDIR_MADE if at least one directory was created. */ + static int +-make_directories (char *file_name) ++make_directories (char *file_name, bool *interdir_made) + { + char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); + char *cursor; /* points into the file name */ +@@ -725,6 +726,7 @@ make_directories (char *file_name) + desired_mode, AT_SYMLINK_NOFOLLOW); + + print_for_mkdir (file_name, cursor - file_name, desired_mode); ++ *interdir_made = true; + parent_end = NULL; + } + else +@@ -879,12 +881,9 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) + FALLTHROUGH; + + case ENOENT: +- /* Attempt creating missing intermediate directories. */ +- if (make_directories (file_name) == 0) +- { +- *interdir_made = true; +- return RECOVER_OK; +- } ++ /* Attempt creating missing intermediate directories. */ ++ if (make_directories (file_name, interdir_made) == 0) ++ return RECOVER_OK; + break; + + default: +@@ -1072,61 +1071,69 @@ extract_dir (char *file_name, int typeflag) + break; + } + +- if (errno == EEXIST +- && (interdir_made ++ if (errno == EEXIST) ++ { ++ if (interdir_made + || keep_directory_symlink_option + || old_files_option == NO_OVERWRITE_DIR_OLD_FILES + || old_files_option == DEFAULT_OLD_FILES +- || old_files_option == OVERWRITE_OLD_FILES)) +- { +- struct stat st; +- st.st_mode = 0; +- +- if (keep_directory_symlink_option +- && is_directory_link (file_name, &st)) +- return 0; +- +- if ((st.st_mode != 0 && fstatat_flags == 0) +- || deref_stat (file_name, &st) == 0) ++ || old_files_option == OVERWRITE_OLD_FILES) + { +- current_mode = st.st_mode; +- current_mode_mask = ALL_MODE_BITS; ++ struct stat st; ++ st.st_mode = 0; ++ ++ if (keep_directory_symlink_option ++ && is_directory_link (file_name, &st)) ++ return 0; + +- if (S_ISDIR (current_mode)) ++ if ((st.st_mode != 0 && fstatat_flags == 0) ++ || deref_stat (file_name, &st) == 0) + { +- if (interdir_made) +- { +- repair_delayed_set_stat (file_name, &st); +- return 0; +- } +- else if (old_files_option == NO_OVERWRITE_DIR_OLD_FILES) ++ current_mode = st.st_mode; ++ current_mode_mask = ALL_MODE_BITS; ++ ++ if (S_ISDIR (current_mode)) + { +- /* Temporarily change the directory mode to a safe +- value, to be able to create files in it, should +- the need be. +- */ +- mode = safe_dir_mode (&st); +- status = fd_chmod(-1, file_name, mode, +- AT_SYMLINK_NOFOLLOW, DIRTYPE); +- if (status == 0) ++ if (interdir_made) + { +- /* Store the actual directory mode, to be restored +- later. +- */ +- current_stat_info.stat = st; +- current_mode = mode & ~ current_umask; +- current_mode_mask = MODE_RWX; +- atflag = AT_SYMLINK_NOFOLLOW; +- break; ++ repair_delayed_set_stat (file_name, &st); ++ return 0; + } +- else ++ else if (old_files_option == NO_OVERWRITE_DIR_OLD_FILES) + { +- chmod_error_details (file_name, mode); ++ /* Temporarily change the directory mode to a safe ++ value, to be able to create files in it, should ++ the need be. ++ */ ++ mode = safe_dir_mode (&st); ++ status = fd_chmod (-1, file_name, mode, ++ AT_SYMLINK_NOFOLLOW, DIRTYPE); ++ if (status == 0) ++ { ++ /* Store the actual directory mode, to be restored ++ later. ++ */ ++ current_stat_info.stat = st; ++ current_mode = mode & ~ current_umask; ++ current_mode_mask = MODE_RWX; ++ atflag = AT_SYMLINK_NOFOLLOW; ++ break; ++ } ++ else ++ { ++ chmod_error_details (file_name, mode); ++ } + } ++ break; + } +- break; + } + } ++ else if (old_files_option == UNLINK_FIRST_OLD_FILES) ++ { ++ status = 0; ++ break; ++ } ++ + errno = EEXIST; + } + +@@ -1978,11 +1985,12 @@ rename_directory (char *src, char *dst) + else + { + int e = errno; ++ bool interdir_made; + + switch (e) + { + case ENOENT: +- if (make_directories (dst) == 0) ++ if (make_directories (dst, &interdir_made) == 0) + { + if (renameat (chdir_fd, src, chdir_fd, dst) == 0) + return true; +-- +2.31.1 + diff --git a/Fix-savannah-bug-63250.patch b/Fix-savannah-bug-63250.patch new file mode 100644 index 0000000000000000000000000000000000000000..9ba0f523b95d90ea23adf9bcec0cd8e8f8399470 --- /dev/null +++ b/Fix-savannah-bug-63250.patch @@ -0,0 +1,25 @@ +From 02f9af1b8df67e55ceb19ea1465d210a2fa1f02c Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sat, 22 Oct 2022 11:14:53 +0300 +Subject: [PATCH] Fix savannah bug #63250 + +* src/buffer.c (magic): Add signature of LZMA files produced by xz. +--- + src/buffer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/buffer.c b/src/buffer.c +index 36b9c9c0..4edaef0b 100644 +--- a/src/buffer.c ++++ b/src/buffer.c +@@ -308,6 +308,7 @@ static struct zip_magic const magic[] = { + { ct_bzip2, 3, "BZh" }, + { ct_lzip, 4, "LZIP" }, + { ct_lzma, 6, "\xFFLZMA" }, ++ { ct_lzma, 3, "\x5d\x00\x00" }, + { ct_lzop, 4, "\211LZO" }, + { ct_xz, 6, "\xFD" "7zXZ" }, + { ct_zstd, 4, "\x28\xB5\x2F\xFD" }, +-- +2.31.1 + diff --git a/Fix-savannah-bug-63567.patch b/Fix-savannah-bug-63567.patch new file mode 100644 index 0000000000000000000000000000000000000000..d007ad4009d9d629ee9aa9e52e6d08d1e164392a --- /dev/null +++ b/Fix-savannah-bug-63567.patch @@ -0,0 +1,28 @@ +From e89c7a45eba7644693870b613386baa45d624e6a Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Mon, 26 Dec 2022 23:44:40 +0200 +Subject: [PATCH 2/9] Fix savannah bug #63567 + +* src/buffer.c (short_read): Increase records_read only if a full +record has been read. +--- + src/buffer.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/buffer.c b/src/buffer.c +index 4edaef0b..1eb9dae8 100644 +--- a/src/buffer.c ++++ b/src/buffer.c +@@ -982,7 +982,8 @@ short_read (size_t status) + } + + record_end = record_start + (record_size - left) / BLOCKSIZE; +- records_read++; ++ if (left == 0) ++ records_read++; + } + + /* Flush the current buffer to/from the archive. */ +-- +2.31.1 + diff --git a/Fix-the-use-of-options-with-trailing-slash-in-files-.patch b/Fix-the-use-of-options-with-trailing-slash-in-files-.patch new file mode 100644 index 0000000000000000000000000000000000000000..a39b1ed7be0f88bcc6543b612cb7d661755be764 --- /dev/null +++ b/Fix-the-use-of-options-with-trailing-slash-in-files-.patch @@ -0,0 +1,37 @@ +From e5bc23efcc90700710f97379d3116febc5705e19 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Thu, 1 Jul 2021 09:38:35 +0300 +Subject: [PATCH] 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. +--- + src/names.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/names.c b/src/names.c +index 503facb4..7773163c 100644 +--- a/src/names.c ++++ b/src/names.c +@@ -980,7 +980,6 @@ read_name_from_file (struct name_elt *ent) + if (counter == name_buffer_length) + name_buffer = x2realloc (name_buffer, &name_buffer_length); + name_buffer[counter] = 0; +- chopslash (name_buffer); + return (counter == 0 && c == EOF) ? file_list_end : file_list_success; + } + +@@ -1060,6 +1059,7 @@ read_next_name (struct name_elt *ent, struct name_elt *ret) + return 1; + } + } ++ chopslash (name_buffer); + ret->type = NELT_NAME; + ret->v.name = name_buffer; + return 0; +-- +2.31.1 + diff --git a/Fix-undefined-behavior-on-bad-extended-header.patch b/Fix-undefined-behavior-on-bad-extended-header.patch new file mode 100644 index 0000000000000000000000000000000000000000..89f6f13e6a379992b336605d0602c4700004c0d3 --- /dev/null +++ b/Fix-undefined-behavior-on-bad-extended-header.patch @@ -0,0 +1,27 @@ +From de64229632a333bca312df70fd58ca8970f10414 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 27 Oct 2022 13:14:52 -0700 +Subject: [PATCH] Fix undefined behavior on bad extended header + +* src/xheader.c (locate_handler): Avoid undefined behavior +if strlen(keyword) < kwlen. +--- + src/xheader.c | 1 ++-- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/src/xheader.c b/src/xheader.c +index dfab6500..08cb474a 100644 +--- a/src/xheader.c ++++ b/src/xheader.c +@@ -561,7 +561,7 @@ locate_handler (char const *keyword) + if (p->prefix) + { + size_t kwlen = strlen (p->keyword); +- if (keyword[kwlen] == '.' && strncmp (p->keyword, keyword, kwlen) == 0) ++ if (strncmp (p->keyword, keyword, kwlen) == 0 && keyword[kwlen] == '.') + return p; + } + else +-- +2.31.1 + diff --git a/Fix-unlikely-uninitalized-var-bug-with-sparse-file.patch b/Fix-unlikely-uninitalized-var-bug-with-sparse-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..aff8f48575b9ad30bbb9e8d7fb424dd26ec7ee78 --- /dev/null +++ b/Fix-unlikely-uninitalized-var-bug-with-sparse-file.patch @@ -0,0 +1,32 @@ +From 55f2a0772e08b9febac3ac0de5cb048d4c60d2f5 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sun, 28 Feb 2021 23:23:16 -0800 +Subject: [PATCH] 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'. +--- + src/sparse.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/sparse.c b/src/sparse.c +index 61561f45..22ba32fa 100644 +--- a/src/sparse.c ++++ b/src/sparse.c +@@ -568,7 +568,10 @@ sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size) + 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; +-- +2.31.1 + diff --git a/Handle-invalid-sparse-entries-in-pax-header.patch b/Handle-invalid-sparse-entries-in-pax-header.patch new file mode 100644 index 0000000000000000000000000000000000000000..c9814b5800112bf11534be3b8504433abf4aec74 --- /dev/null +++ b/Handle-invalid-sparse-entries-in-pax-header.patch @@ -0,0 +1,30 @@ +From cc8f5f78b2437f50f0972433275b910b10d46717 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sat, 28 Aug 2021 14:15:25 +0300 +Subject: [PATCH] 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. +--- + src/sparse.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/sparse.c b/src/sparse.c +index 2ebc6212..7587edb2 100644 +--- a/src/sparse.c ++++ b/src/sparse.c +@@ -1309,7 +1309,9 @@ pax_decode_header (struct tar_sparse_file *file) + } + sp.offset = u; + COPY_BUF (blk,nbuf,p); +- if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t))) ++ if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)) ++ || INT_ADD_OVERFLOW (sp.offset, u) ++ || file->stat_info->stat.st_size < sp.offset + u) + { + ERROR ((0, 0, _("%s: malformed sparse archive member"), + file->stat_info->orig_file_name)); +-- +2.31.1 + diff --git a/tar-1.28-atime-rofs.patch b/tar-1.28-atime-rofs.patch new file mode 100644 index 0000000000000000000000000000000000000000..f32114a419ecece1f8001d2b0f50930f26088cd7 --- /dev/null +++ b/tar-1.28-atime-rofs.patch @@ -0,0 +1,34 @@ +From 71769b9ea3c12b7fbb39fee2e9f4a4c1c36c0d0b Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 28 Jul 2014 08:13:31 +0200 +Subject: [PATCH 4/9] utime & read-only FS + +Ignore errors from setting utime() for source file on read-only +file-system. + +Resolves: #500742 + +Upstream bugreport (still downstream): +http://lists.gnu.org/archive/html/bug-tar/2009-06/msg00016.html + +--- + src/create.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/create.c b/src/create.c +index e2f4ede..f644f23 100644 +--- a/src/create.c ++++ b/src/create.c +@@ -1824,7 +1824,8 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p) + } + else if (atime_preserve_option == replace_atime_preserve + && fd && (is_dir || original_size != 0) +- && set_file_atime (fd, parentfd, name, st->atime) != 0) ++ && set_file_atime (fd, parentfd, name, st->atime) != 0 ++ && errno != EROFS ) + utime_error (p); + } + +-- +1.9.3 + diff --git a/tar-1.28-document-exclude-mistakes.patch b/tar-1.28-document-exclude-mistakes.patch new file mode 100644 index 0000000000000000000000000000000000000000..73f4ed34941b464a9a2dad29684d52cf7a3e77a8 --- /dev/null +++ b/tar-1.28-document-exclude-mistakes.patch @@ -0,0 +1,97 @@ +From 18112ded916cf62b3bd3c0ffb9530e4ade3d2209 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 28 Jul 2014 08:16:33 +0200 +Subject: [PATCH 7/9] Document exclude mistakes with + +.. usually with --no-wildcards-match-slash & --anchored options. + +Upstream bugreport (still downstream): +http://www.mail-archive.com/bug-tar@gnu.org/msg04488.html + +Related: #903666 + +--- + doc/tar.texi | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 67 insertions(+) + +diff --git a/doc/tar.texi b/doc/tar.texi +index a000f3f..2695d22 100644 +--- a/doc/tar.texi ++++ b/doc/tar.texi +@@ -8051,6 +8051,73 @@ The following table summarizes pattern-matching default values: + @item Exclusion @tab @option{--wildcards --no-anchored --wildcards-match-slash} + @end multitable + ++@subsubsection Wildcard matching confusion ++Using of @option{--[no-]anchored} and @option{--[no-]wildcards-match-slash} ++was proven to make confusion. The reasons for this are probably different ++default setting for inclusion and exclusion patterns (in general: you shouldn't ++rely on defaults if possible) and maybe also because when using any of these two ++options, the position on command line matters (these options should be placed ++prior to the member name on command line). ++ ++@noindent ++Consider following directory structure: ++ ++@smallexample ++$ find path/ | sort ++path/ ++path/file1 ++path/file2 ++path/subpath ++path/subpath/file1 ++path/subpath/file2 ++path/subpath2 ++path/subpath2/file1 ++path/subpath2/file2 ++@end smallexample ++ ++@noindent ++To archive full directory @samp{path} except all files named @samp{file1} may be ++reached by any of the two following commands: ++ ++@smallexample ++$ tar -cf a.tar --no-wildcards-match-slash --no-anchored path \ ++ --exclude='*/file1' ++$ tar -cf a.tar --wildcards-match-slash path --exclude='*/file1' ++@end smallexample ++ ++@noindent ++Note that the @option{--wildcards-match-slash} and @option{--no-anchored} may be ++omitted as it is default for @option{--exclude}. Anyway, we usually want just ++concrete file (or rather subset of files with the same name). Assume we want ++exclude only files named @samp{file1} from the first subdirectory level. ++Following command obviously does not work (it still excludes all files having ++@samp{file1} name): ++ ++@smallexample ++$ tar -cf a.tar --no-wildcards-match-slash path \ ++ --exclude='*/file1' | sort ++@end smallexample ++ ++@noindent ++This is because the @option{--no-anchored} is set by default for exclusion. ++What you need to fix is to put @option{--anchored} before pathname: ++ ++@smallexample ++$ tar -cvf a.tar --no-wildcards-match-slash --anchored path \ ++ --exclude='*/file1' | sort ++path/ ++path/file2 ++path/subpath1/ ++path/subpath1/file1 ++path/subpath1/file2 ++path/subpath2/ ++path/subpath2/file1 ++path/subpath2/file2 ++@end smallexample ++ ++@noindent ++Similarly you can exclude second level by specifying @samp{*/*/file1}. ++ + @node quoting styles + @section Quoting Member Names + +-- +1.9.3 + diff --git a/tar-1.28-loneZeroWarning.patch b/tar-1.28-loneZeroWarning.patch new file mode 100644 index 0000000000000000000000000000000000000000..c142ea37e31270ee7ffcd52154b3a1c34dcb9537 --- /dev/null +++ b/tar-1.28-loneZeroWarning.patch @@ -0,0 +1,43 @@ +From 60d08c6d82f0c33a6704c79afd416902eb8c663f Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 28 Jul 2014 08:08:50 +0200 +Subject: [PATCH 1/9] Stop issuing lone zero block warnings (downstram) + +Resolves: #135601 + +--- + src/list.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/list.c b/src/list.c +index b4277e0..0c7a740 100644 +--- a/src/list.c ++++ b/src/list.c +@@ -240,6 +240,14 @@ read_and (void (*do_something) (void)) + + if (!ignore_zeros_option) + { ++ /* ++ * According to POSIX tar specs, this is wrong, but on the web ++ * there are some tar specs that can trigger this, and some tar ++ * implementations create tars according to that spec. For now, ++ * let's not be pedantic about issuing the warning. ++ */ ++#if 0 ++ + char buf[UINTMAX_STRSIZE_BOUND]; + + status = read_header (¤t_header, ¤t_stat_info, +@@ -249,6 +257,9 @@ read_and (void (*do_something) (void)) + WARNOPT (WARN_ALONE_ZERO_BLOCK, + (0, 0, _("A lone zero block at %s"), + STRINGIFY_BIGINT (current_block_ordinal (), buf))); ++#endif ++ status = read_header (¤t_header, ¤t_stat_info, ++ read_header_auto); + break; + } + status = prev_status; +-- +1.9.3 + diff --git a/tar-1.28-vfatTruncate.patch b/tar-1.28-vfatTruncate.patch new file mode 100644 index 0000000000000000000000000000000000000000..734fbc4e4c23df4c709d5ab198663d61074613f3 --- /dev/null +++ b/tar-1.28-vfatTruncate.patch @@ -0,0 +1,52 @@ +From 90f446f9b04ab820a99b9408e68c01dc6b57056c Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 28 Jul 2014 08:10:10 +0200 +Subject: [PATCH 2/9] vfat-like FS & sparse (still downstream) + +Fix extracting sparse files to a file system like vfat, when +ftruncate may fail to grow the size of a file. Still downstram, +(do we need this now? ftruncate & vfat works is now OK). + +Resolves: #179507 + +Upstream bugreport: +http://lists.gnu.org/archive/html/bug-tar/2006-02/msg00000.html + +--- + src/system.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/system.c b/src/system.c +index 9414233..37e9a3e 100644 +--- a/src/system.c ++++ b/src/system.c +@@ -243,8 +243,25 @@ sys_compare_links (struct stat *link_data, struct stat *stat_data) + int + sys_truncate (int fd) + { ++ struct stat st; + off_t pos = lseek (fd, (off_t) 0, SEEK_CUR); +- return pos < 0 ? -1 : ftruncate (fd, pos); ++ ++ if ( pos < 0) ++ return -1; ++ ++ if ( ftruncate(fd, pos) && errno == EPERM ) { ++ /* wrapper around ftruncate: ++ * ftruncate may fail to grow the size of a file with some OS and filesystem ++ * combinations. Linux and vfat/fat is one example. If this is the case do ++ * a write to grow the file to the desired length. ++ */ ++ if( (fstat( fd, &st ) == -1) || ++ (st.st_size >= pos) || ++ (lseek( fd, pos - 1, SEEK_SET) == (off_t)-1) || ++ (write( fd, "\0", 1) == -1) ) ++ return -1; ++ } ++ return 0; + } + + /* Return nonzero if NAME is the name of a regular file, or if the file +-- +1.9.3 + diff --git a/tar-1.29-wildcards.patch b/tar-1.29-wildcards.patch new file mode 100644 index 0000000000000000000000000000000000000000..40f94253a09e0a0aec68973f351146879646b2c8 --- /dev/null +++ b/tar-1.29-wildcards.patch @@ -0,0 +1,107 @@ +From ae0730a98f7269a7bf7adb6047aa421939b290b7 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 28 Jul 2014 08:12:14 +0200 +Subject: [PATCH] wildcard defaults (downstram; compatibility) + +Change inclusion defaults of tar to "wildcards --anchored +--wildcards-match-slash" for compatibility reasons. + +Resolves: #206841 +--- + doc/tar.texi | 5 ++++- + src/names.c | 15 +++++---------- + tests/exclude01.at | 1 + + 3 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/doc/tar.texi b/doc/tar.texi +index a8969e0..0185157 100644 +--- a/doc/tar.texi ++++ b/doc/tar.texi +@@ -8439,7 +8439,7 @@ The following table summarizes pattern-matching default values: + + @multitable @columnfractions .3 .7 + @headitem Members @tab Default settings +-@item Inclusion @tab @option{--no-wildcards --anchored --no-wildcards-match-slash} ++@item Inclusion @tab @option{--wildcards --anchored --wildcards-match-slash} + @item Exclusion @tab @option{--wildcards --no-anchored --wildcards-match-slash} + @end multitable + +@@ -12915,6 +12915,9 @@ version of this document is available at + @table @asis + @item Use of globbing patterns when listing and extracting. + ++Note: Following is true for original unpatched GNU tar. ++For compatibility reasons, the old behavior was preserved. ++ + Previous versions of GNU tar assumed shell-style globbing when + extracting from or listing an archive. For example: + +diff --git a/src/names.c b/src/names.c +index 037b869..d96ad71 100644 +--- a/src/names.c ++++ b/src/names.c +@@ -146,7 +146,7 @@ static struct argp_option names_options[] = { + {"no-wildcards", NO_WILDCARDS_OPTION, 0, 0, + N_("verbatim string matching"), GRID_MATCH }, + {"wildcards-match-slash", WILDCARDS_MATCH_SLASH_OPTION, 0, 0, +- N_("wildcards match '/' (default for exclusion)"), GRID_MATCH }, ++ N_("wildcards match '/' (default)"), GRID_MATCH }, + {"no-wildcards-match-slash", NO_WILDCARDS_MATCH_SLASH_OPTION, 0, 0, + N_("wildcards do not match '/'"), GRID_MATCH }, + +@@ -195,8 +195,7 @@ names_parse_opt (int key, char *arg, struct argp_state *state) + /* Wildcard matching settings */ + enum wildcards + { +- default_wildcards, /* For exclusion == enable_wildcards, +- for inclusion == disable_wildcards */ ++ default_wildcards, /* enable_wildcards */ + disable_wildcards, + enable_wildcards + }; +@@ -214,7 +213,7 @@ static int include_anchored = EXCLUDE_ANCHORED; + | recursion_option) + + #define INCLUDE_OPTIONS \ +- (((wildcards == enable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ ++ (((wildcards != disable_wildcards) ? EXCLUDE_WILDCARDS : 0) \ + | include_anchored \ + | matching_flags \ + | recursion_option) +@@ -1234,8 +1233,7 @@ regex_usage_warning (const char *name) + + /* Warn about implicit use of the wildcards in command line arguments. + (Default for tar prior to 1.15.91, but changed afterwards) */ +- if (wildcards == default_wildcards +- && fnmatch_pattern_has_wildcards (name, 0)) ++ if (0 && fnmatch_pattern_has_wildcards (name, 0)) + { + warned_once = 1; + WARN ((0, 0, +@@ -1618,10 +1616,7 @@ collect_and_sort_names (void) + + if (name->found_count || name->directory) + continue; +- if (name->matching_flags & EXCLUDE_WILDCARDS) +- /* NOTE: EXCLUDE_ANCHORED is not relevant here */ +- /* FIXME: just skip regexps for now */ +- continue; ++ + chdir_do (name->change_dir); + + if (name->name[0] == 0) +diff --git a/tests/exclude01.at b/tests/exclude01.at +index c3cd10b..c590047 100644 +--- a/tests/exclude01.at ++++ b/tests/exclude01.at +@@ -61,6 +61,7 @@ testdir/dir2/file2 + testdir/dir3/ + NEXT + testdir/dir1/* ++testdir/dir1/file1 + NEXT + testdir/dir1/* + NEXT +-- +2.5.5 + diff --git a/tar-1.33-fix-capabilities-test.patch b/tar-1.33-fix-capabilities-test.patch new file mode 100644 index 0000000000000000000000000000000000000000..5835c2dcf74068f0d536a4a45b6bd77e3a69492a --- /dev/null +++ b/tar-1.33-fix-capabilities-test.patch @@ -0,0 +1,34 @@ +From: Pavel Raiskup +Date: Tue, 16 Feb 2021 08:10:22 +0100 +Subject: [PATCH] Related discussion in the Fedora pull-request: + https://src.fedoraproject.org/rpms/tar/pull-request/8 + +Upstream report: +https://www.mail-archive.com/bug-tar@gnu.org/msg05943.html + +* tests/capabs_raw01.at: Newer systems (currently e.g. Fedora 34) +print getcap output in format CAP=VAL, not CAP+VAL. +--- + tests/capabs_raw01.at | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/capabs_raw01.at b/tests/capabs_raw01.at +index a1d9411..d3da923 100644 +--- a/tests/capabs_raw01.at ++++ b/tests/capabs_raw01.at +@@ -45,10 +45,10 @@ rm -rf dir + tar --xattrs --xattrs-include='*' -xf archive.tar + + # Newer systems print = instead of + here +-getcap dir/file | sed 's/+/=/' ++getcap dir/file | sed -e 's/+/=/' -e 's|dir/file = |dir/file |' + ], + [0], +-[dir/file = cap_chown=ei ++[dir/file cap_chown=ei + ]) + + AT_CLEANUP +-- +2.26.0 + diff --git a/tar-fix-exclude-vcs-ignores-memory.patch b/tar-fix-exclude-vcs-ignores-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..b40f6e9c568a3ba2922f0a6ac8037e58c1ea4b16 --- /dev/null +++ b/tar-fix-exclude-vcs-ignores-memory.patch @@ -0,0 +1,30 @@ +From 7324326b1df67831b39a597eb10f67a5369aa189 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Aur=C3=A9lien=20Martin?= +Date: Fri, 7 Oct 2022 21:08:40 +0200 +Subject: [PATCH] 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 +--- + src/exclist.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/exclist.c b/src/exclist.c +index 80450bb7..1be08cbe 100644 +--- a/src/exclist.c ++++ b/src/exclist.c +@@ -203,6 +203,7 @@ cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data) + struct wordsplit ws; + size_t i; + ++ options |= EXCLUDE_ALLOC; + if (wordsplit (pattern, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)) + return; +-- +2.31.1 + diff --git a/tar-fix-race-condition-1.patch b/tar-fix-race-condition-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..2d7a47c4c4540ebd7b204b3fc8e86884909db77b --- /dev/null +++ b/tar-fix-race-condition-1.patch @@ -0,0 +1,117 @@ +From 79a442d7b0e92622794bfa41dee18a28e450a0dc Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 9 Jun 2022 22:09:34 -0700 +Subject: [PATCH 1/2] 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. +--- + src/extract.c | 61 ++++++++++++++++++++++++++++++++------------------- + 1 files changed, 39 insertions(+), 22 deletions(-) + +diff --git a/src/extract.c b/src/extract.c +index e7be463e..0753decf 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -636,8 +636,7 @@ fixup_delayed_set_stat (char const *src, char const *dst) + } + } + +-/* After a file/link/directory creation has failed, see if +- it's because some required directory was not present, and if so, ++/* After a file/link/directory creation has failed due to ENOENT, + create all required directories. Return zero if all the required + directories were created, nonzero (issuing a diagnostic) otherwise. + Set *INTERDIR_MADE if at least one directory was created. */ +@@ -646,6 +645,8 @@ make_directories (char *file_name, bool *interdir_made) + { + char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); + char *cursor; /* points into the file name */ ++ char *parent_end = NULL; ++ int parent_errno; + + for (cursor = cursor0; *cursor; cursor++) + { +@@ -685,31 +686,47 @@ make_directories (char *file_name, bool *interdir_made) + + print_for_mkdir (file_name, cursor - file_name, desired_mode); + *interdir_made = true; ++ parent_end = NULL; + } +- else if (errno == EEXIST) +- status = 0; + else +- { +- /* Check whether the desired file exists. Even when the +- file exists, mkdir can fail with some errno value E other +- than EEXIST, so long as E describes an error condition +- that also applies. */ +- int e = errno; +- struct stat st; +- status = fstatat (chdir_fd, file_name, &st, 0); +- if (status) +- { +- errno = e; +- mkdir_error (file_name); +- } +- } ++ switch (errno) ++ { ++ case ELOOP: case ENAMETOOLONG: case ENOENT: case ENOTDIR: ++ /* FILE_NAME doesn't exist and couldn't be created; fail now. */ ++ mkdir_error (file_name); ++ *cursor = '/'; ++ return status; ++ ++ default: ++ /* FILE_NAME may be an existing directory so do not fail now. ++ Instead, arrange to check at loop exit, assuming this is ++ the last loop iteration. */ ++ parent_end = cursor; ++ parent_errno = errno; ++ break; ++ } + + *cursor = '/'; +- if (status) +- return status; + } + +- return 0; ++ if (!parent_end) ++ return 0; ++ ++ /* Although we did not create the parent directory, some other ++ process may have created it, so check whether it exists now. */ ++ *parent_end = '\0'; ++ struct stat st; ++ int stat_status = fstatat (chdir_fd, file_name, &st, 0); ++ if (!stat_status && !S_ISDIR (st.st_mode)) ++ stat_status = -1; ++ if (stat_status) ++ { ++ errno = parent_errno; ++ mkdir_error (file_name); ++ } ++ *parent_end = '/'; ++ ++ return stat_status; + } + + /* Return true if FILE_NAME (with status *STP, if STP) is not a +@@ -824,7 +841,7 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) + + case ENOENT: + /* Attempt creating missing intermediate directories. */ +- if (make_directories (file_name, interdir_made) == 0 && *interdir_made) ++ if (make_directories (file_name, interdir_made) == 0) + return RECOVER_OK; + break; + +-- +2.31.1 + diff --git a/tar-fix-race-condition-2.patch b/tar-fix-race-condition-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..abb7cf5bad72c6e5b85fedcd24371f6559f09728 --- /dev/null +++ b/tar-fix-race-condition-2.patch @@ -0,0 +1,71 @@ +From 79d1ac38c19faad64f0e993180bf1ad27f217072 Mon Sep 17 00:00:00 2001 +From: James Abbatiello +Date: Fri, 10 Jun 2022 18:25:13 -0700 +Subject: [PATCH 2/2] 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 +--- + src/extract.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/extract.c b/src/extract.c +index 0753decf..fda4617d 100644 +--- a/src/extract.c ++++ b/src/extract.c +@@ -638,10 +638,9 @@ fixup_delayed_set_stat (char const *src, char const *dst) + + /* After a file/link/directory creation has failed due to ENOENT, + create all required directories. Return zero if all the required +- directories were created, nonzero (issuing a diagnostic) otherwise. +- Set *INTERDIR_MADE if at least one directory was created. */ ++ directories were created, nonzero (issuing a diagnostic) otherwise. */ + static int +-make_directories (char *file_name, bool *interdir_made) ++make_directories (char *file_name) + { + char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); + char *cursor; /* points into the file name */ +@@ -685,7 +684,6 @@ make_directories (char *file_name, bool *interdir_made) + desired_mode, AT_SYMLINK_NOFOLLOW); + + print_for_mkdir (file_name, cursor - file_name, desired_mode); +- *interdir_made = true; + parent_end = NULL; + } + else +@@ -841,8 +839,11 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) + + case ENOENT: + /* Attempt creating missing intermediate directories. */ +- if (make_directories (file_name, interdir_made) == 0) +- return RECOVER_OK; ++ if (make_directories (file_name) == 0) ++ { ++ *interdir_made = true; ++ return RECOVER_OK; ++ } + break; + + default: +@@ -1944,12 +1945,11 @@ rename_directory (char *src, char *dst) + else + { + int e = errno; +- bool interdir_made; + + switch (e) + { + case ENOENT: +- if (make_directories (dst, &interdir_made) == 0) ++ if (make_directories (dst) == 0) + { + if (renameat (chdir_fd, src, chdir_fd, dst) == 0) + return true; +-- +2.31.1 + diff --git a/tar.spec b/tar.spec index 653072bade7ab931c0f75464b6cf54c04cc350ef..69a85b260eac502f450621c68a48121557013d2f 100644 --- a/tar.spec +++ b/tar.spec @@ -1,4 +1,4 @@ -%define anolis_release 2 +%define anolis_release 3 %bcond_without selinux %bcond_without check @@ -14,6 +14,54 @@ URL: https://www.gnu.org/software/tar/ Source0: https://ftp.gnu.org/gnu/tar/tar-%{version}.tar.xz Source1: https://ftp.gnu.org/gnu/tar/tar-%{version}.tar.xz.sig +Patch1: tar-1.28-loneZeroWarning.patch +Patch2: tar-1.28-vfatTruncate.patch +Patch3: tar-1.29-wildcards.patch +Patch4: tar-1.28-atime-rofs.patch +Patch9: tar-1.28-document-exclude-mistakes.patch +Patch10: tar-1.33-fix-capabilities-test.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=55f2a0772e08b9febac3ac0de5cb048d4c60d2f5 +Patch11: Fix-unlikely-uninitalized-var-bug-with-sparse-file.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=e5bc23efcc90700710f97379d3116febc5705e19 +Patch12: Fix-the-use-of-options-with-trailing-slash-in-files-.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=93082d6eb84a086b02c7a8607f2553d5d9d0a93c +Patch13: Fix-crash-on-invalid-command-line-argument.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=193eb8f81d8fbf7b1f3bd2b93f60882e56325d72 +Patch14: transform-fix-replacement-of-particular-pattern-inst.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=cc8f5f78b2437f50f0972433275b910b10d46717 +Patch15: Handle-invalid-sparse-entries-in-pax-header.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=a339f05cd269013fa133d2f148d73f6f7d4247e4 +Patch16: Fix-handling-of-extended-header-prefixes.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=7958eb97e6ca2aca66c2d5a45b7e803f31646e4e +Patch17: Fix-null-rereference-if-low-memory.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=79a442d7b0e92622794bfa41dee18a28e450a0dc +Patch18: tar-fix-race-condition-1.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=79d1ac38c19faad64f0e993180bf1ad27f217072 +Patch19: tar-fix-race-condition-2.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=d935dc7d1c150b3425dd43dc13a4dd2e2b712c26 +Patch20: Avoid-EOVERFLOW-problems-in-some-symlink-tests.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=66be5a789e70f911170017754fc51e499998f90e +Patch21: Avoid-excess-lseek-etc.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=0b74885e81b90d6ab4890b195dce99ca9109fe59 +Patch22: Fix-bug-with-x-xattr-read-only-files.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=f8e14746d2ca72804a4520c059d7bf65ca00c5ac +Patch23: Fix-delete-bug-with-short-reads.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=bc277c7069cdebb6a6140326636555267b142e0e +Patch24: Fix-data-loss-when-acting-as-filter.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=7324326b1df67831b39a597eb10f67a5369aa189 +Patch25: tar-fix-exclude-vcs-ignores-memory.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=02f9af1b8df67e55ceb19ea1465d210a2fa1f02c +Patch26: Fix-savannah-bug-63250.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=17debecd7300e94f590b8ce167a8c0735cb6d57d +Patch27: Fix-savannah-bug-63123.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=de64229632a333bca312df70fd58ca8970f10414 +Patch28: Fix-undefined-behavior-on-bad-extended-header.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=24c830696527566742f1c8cac70d53086afca599 +Patch29: Fix-Af-F-bug-when-F-is-not-a-regular-file.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=e89c7a45eba7644693870b613386baa45d624e6a +Patch30: Fix-savannah-bug-63567.patch +# https://git.savannah.gnu.org/cgit/tar.git/commit/?id=71d1619abd8de3d67105230a301264d0de86c0c7 +Patch31: Fix-assume-include.patch BuildRequires: make BuildRequires: gcc @@ -53,7 +101,7 @@ BuildArch: noarch The %{name}-doc package contains documentation files for %{name}. %prep -%autosetup +%autosetup -p1 autoreconf -v # Keep only entries related to the latest release. @@ -86,6 +134,7 @@ rm -f $RPM_BUILD_ROOT%{_mandir}/man8/rmt.8* %find_lang %name +%generate_compatibility_deps %check %if %{with check} @@ -106,8 +155,10 @@ make check || ( %files -f %{name}.lang %license COPYING +%dir %{abidir} %{_bindir}/tar %{_bindir}/gtar +%{abidir}/tar-option.list %{_mandir}/man1/tar.1* %{_mandir}/man1/gtar.1* %{_infodir}/tar.info* @@ -116,6 +167,10 @@ make check || ( %doc AUTHORS README THANKS NEWS ChangeLog %changelog +* Tue Dec 06 2022 Shawn Wang - 2:1.34-3 +- add abi/api info +- add some patches + * Mon Oct 10 2022 mgb01105731 - 1.34-2 - add doc package diff --git a/transform-fix-replacement-of-particular-pattern-inst.patch b/transform-fix-replacement-of-particular-pattern-inst.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ddc50b1e177dca4e70619aaa7c3769b37cc4705 --- /dev/null +++ b/transform-fix-replacement-of-particular-pattern-inst.patch @@ -0,0 +1,42 @@ +From 193eb8f81d8fbf7b1f3bd2b93f60882e56325d72 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +Date: Sat, 28 Aug 2021 09:13:05 +0300 +Subject: [PATCH] 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. +--- + src/transform.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/transform.c b/src/transform.c +index 2b602353..2cc927e8 100644 +--- a/src/transform.c ++++ b/src/transform.c +@@ -493,9 +493,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) + { +@@ -504,6 +501,9 @@ _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) +-- +2.31.1 +