From: Sergey Poznyakoff Date: Sat, 15 Jul 2023 15:06:27 +0000 (+0300) Subject: Use full-read instead of safe-read X-Git-Tag: v1.35~1 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=e7a5e124455fd4d15193cdea360819d12fdbeeb5;p=thirdparty%2Ftar.git 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. --- diff --git a/gnulib.modules b/gnulib.modules index 064f9349..09300e29 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -79,7 +79,7 @@ readlinkat renameat root-uid rpmatch -safe-read +full-read savedir selinux-at setenv diff --git a/src/common.h b/src/common.h index c79b3b63..9451e132 100644 --- a/src/common.h +++ b/src/common.h @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #define obstack_chunk_alloc xmalloc diff --git a/src/misc.c b/src/misc.c index b7b99e5f..73c04646 100644 --- a/src/misc.c +++ b/src/misc.c @@ -821,7 +821,7 @@ deref_stat (char const *name, struct stat *buf) size_t blocking_read (int fd, void *buf, size_t count) { - size_t bytes = safe_read (fd, buf, count); + size_t bytes = full_read (fd, buf, count); #if defined F_SETFL && O_NONBLOCK if (bytes == SAFE_READ_ERROR && errno == EAGAIN) @@ -829,10 +829,12 @@ blocking_read (int fd, void *buf, size_t count) 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); + bytes = full_read (fd, buf, count); } #endif + if (bytes == 0 && errno != 0) + bytes = SAFE_READ_ERROR; return bytes; } diff --git a/src/sparse.c b/src/sparse.c index ec7152a6..ec19636c 100644 --- a/src/sparse.c +++ b/src/sparse.c @@ -415,7 +415,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i) size_t bytes_read; blk = find_next_block (); - bytes_read = safe_read (file->fd, blk->buffer, bufsize); + bytes_read = full_read (file->fd, blk->buffer, bufsize); if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, @@ -427,27 +427,39 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i) } 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; + if (errno != 0) + { + read_diag_details (file->stat_info->orig_file_name, + (file->stat_info->sparse_map[i].offset + + file->stat_info->sparse_map[i].numbytes + - bytes_left), + bufsize); + return false; + } else - n = file->stat_info->stat.st_size - - (file->stat_info->sparse_map[i].offset - + file->stat_info->sparse_map[i].numbytes - - bytes_left); - - WARNOPT (WARN_FILE_SHRANK, - (0, 0, - ngettext ("%s: File shrank by %s byte; padding with zeros", - "%s: File shrank by %s bytes; padding with zeros", - n), - quotearg_colon (file->stat_info->orig_file_name), - STRINGIFY_BIGINT (n, buf))); - if (! ignore_failed_read_option) - set_exit_status (TAREXIT_DIFFERS); - return false; + { + char buf[UINTMAX_STRSIZE_BOUND]; + struct stat st; + size_t n; + if (fstat (file->fd, &st) == 0) + n = file->stat_info->stat.st_size - st.st_size; + else + n = file->stat_info->stat.st_size + - (file->stat_info->sparse_map[i].offset + + file->stat_info->sparse_map[i].numbytes + - bytes_left); + + WARNOPT (WARN_FILE_SHRANK, + (0, 0, + ngettext ("%s: File shrank by %s byte; padding with zeros", + "%s: File shrank by %s bytes; padding with zeros", + n), + quotearg_colon (file->stat_info->orig_file_name), + STRINGIFY_BIGINT (n, buf))); + if (! ignore_failed_read_option) + set_exit_status (TAREXIT_DIFFERS); + return false; + } } memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read); @@ -615,7 +627,7 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end) size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg; char diff_buffer[BLOCKSIZE]; - bytes_read = safe_read (file->fd, diff_buffer, rdsize); + bytes_read = full_read (file->fd, diff_buffer, rdsize); if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, @@ -625,7 +637,12 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end) } else if (bytes_read == 0) { - report_difference (file->stat_info, _("Size differs")); + if (errno != 0) + read_diag_details (file->stat_info->orig_file_name, + beg, + rdsize); + else + report_difference (file->stat_info, _("Size differs")); return false; } @@ -668,7 +685,7 @@ check_data_region (struct tar_sparse_file *file, size_t i) } set_next_block_after (blk); file->dumped_size += BLOCKSIZE; - bytes_read = safe_read (file->fd, diff_buffer, rdsize); + bytes_read = full_read (file->fd, diff_buffer, rdsize); if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, @@ -680,7 +697,14 @@ check_data_region (struct tar_sparse_file *file, size_t i) } else if (bytes_read == 0) { - report_difference (¤t_stat_info, _("Size differs")); + 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); + else + report_difference (¤t_stat_info, _("Size differs")); return false; } size_left -= bytes_read; diff --git a/src/update.c b/src/update.c index 0d3b45a3..dc5e5aca 100644 --- a/src/update.c +++ b/src/update.c @@ -60,10 +60,14 @@ append_file (char *file_name) while (true) { union block *start = find_next_block (); - size_t status = safe_read (handle, start->buffer, + size_t status = full_read (handle, start->buffer, available_space_after (start)); if (status == 0) - break; + { + if (errno == 0) + break; + read_fatal (file_name); + } if (status == SAFE_READ_ERROR) read_fatal (file_name); if (status % BLOCKSIZE)