diff -Naur tar-1.13.17/src/buffer.c tar-1.13.17patched/src/buffer.c --- tar-1.13.17/src/buffer.c Fri Jan 7 23:33:43 2000 +++ tar-1.13.17patched/src/buffer.c Tue Jan 16 11:33:43 2001 @@ -243,6 +243,41 @@ return record_end->buffer - pointer->buffer; } +/*----------------------------------------------------------------. +| Read LENGTH bytes at BUFFER from archive ARCHIVE. | +| If read_full_records_option is set, retry to get all of them, | +| if necessary and if not at EOF. | +| Return the actual number of bytes read, zero for immediate EOF, | +| or negative for an error. | +`----------------------------------------------------------------*/ + +static ssize_t +rmtread_full (int archive, char *buffer, size_t length) +{ + ssize_t status; /* result from system call */ + size_t bytes_in_buffer; /* no. of bytes already in buffer */ + + if (!read_full_records_option) + return rmtread (archive, buffer, length); + + bytes_in_buffer = 0; + while (length - bytes_in_buffer > 0) + { + status = rmtread (archive, + &buffer[bytes_in_buffer], + (length - bytes_in_buffer)); + if (status < 0) + return status; + if (!status) + { + hit_eof = 1; + break; + } + bytes_in_buffer += status; + } + return bytes_in_buffer; +} + /*------------------------------------------------------------------. | Close file having descriptor FD, and abort if close unsuccessful. | `------------------------------------------------------------------*/ @@ -590,7 +625,7 @@ read_error_count = 0; error_loop: - status = rmtread (archive, record_start->buffer, record_size); + status = rmtread_full (archive, record_start->buffer, record_size); if (status < 0) { archive_read_error (); @@ -992,10 +1027,36 @@ bytes_written += status; + /* If we have a very small record size, i.e., 1*BLOCKSIZE or + 2*BLOCKSIZE, which is less or equal the length of the volume + header, then we have not yet written any data from the buffer, + only part or all from the volume header. Therefore, we need to + write more records. After that, we know that no partial records + are left, and we set copy_back to 0. */ + if (record_size <= copy_back * BLOCKSIZE) + { + while ((record_start += (record_size / BLOCKSIZE)) <= current_block) + { + status = write_archive_buffer (); + if (status != record_size) + archive_write_error (status); + + bytes_written += status; + } + record_start -= (record_size / BLOCKSIZE); + copy_back = 0; + } + if (copy_back) { record_start += copy_back; - memcpy (current_block, + /* If we had to write a header for a new volume, we have not yet + written the complete buffer. Move the according number of blocks + to the front of the buffer, and adjust the current_block pointer + accordingly. */ + /* Use memmove instead of memcpy, since the memory areas might + overlap. */ + memmove (current_block, record_start + blocking_factor - copy_back, copy_back * BLOCKSIZE); current_block += copy_back; @@ -1110,7 +1171,7 @@ } error_loop: - status = rmtread (archive, record_start->buffer, record_size); + status = rmtread_full (archive, record_start->buffer, record_size); if (status == record_size) return; @@ -1138,7 +1199,7 @@ } vol_error: - status = rmtread (archive, record_start->buffer, record_size); + status = rmtread_full (archive, record_start->buffer, record_size); if (status < 0) { archive_read_error (); @@ -1165,7 +1226,16 @@ } if (verbose_option) fprintf (stdlis, _("Reading %s\n"), quote (cursor->header.name)); - cursor++; + if (++cursor >= record_end) + { /* if the blocking factor is 1, we are already run out of data */ + while ((status = rmtread_full (archive, + record_start->buffer, + record_size)) < 0) + archive_read_error (); + if (status != record_size) + goto short_read; + cursor = record_start; + } } else if (volume_label_option) WARN ((0, 0, _("WARNING: No volume header"))); @@ -1207,7 +1277,17 @@ global_volno--; goto try_volume; } - cursor++; + if (++cursor >= record_end) + { /* if the blocking factor is 1 or 2, we are already run out of + data */ + while ((status = rmtread_full (archive, + record_start->buffer, + record_size)) < 0) + archive_read_error (); + if (status != record_size) + goto short_read; + cursor = record_start; + } } current_block = cursor; return; @@ -1225,13 +1305,17 @@ while (left % BLOCKSIZE != 0) { if (status) - while ((status = rmtread (archive, more, left)) < 0) + while ((status = rmtread_full (archive, more, left)) < 0) archive_read_error (); if (status == 0) { ERROR ((0, 0, _("%d garbage bytes ignored at end of archive"), (int) ((record_size - left) % BLOCKSIZE))); + /* When EOF occurs in this while loop, then the total number of + bytes read is not necessarily zero. So we set the + corresponding flag here for safety, too. */ + hit_eof = 1; break; }