-GNU tar NEWS - User visible changes. 2025-05-06
+GNU tar NEWS - User visible changes. 2025-07-25
Please send GNU tar bug reports to <bug-tar@gnu.org>
\f
version TBD
** Transformations that change case (e.g., --transform='s/.*/\L&/')
now work correctly with multi-byte characters.
+** 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.
return current_block;
}
-/* Indicate that we have used all blocks up thru BLOCK. */
+/* Indicate that we have used all blocks up thru the block addressed by PTR,
+ which may point to any of the bytes in the addressed block.
+ This does nothing if PTR points before the current block. */
void
-set_next_block_after (union block *block)
+set_next_block_after (void *ptr)
{
- while (block >= current_block)
- current_block++;
+ char *p = ptr;
+ ptrdiff_t offset = p - charptr (current_block);
+ if (0 <= offset)
+ current_block += (offset >> LG_BLOCKSIZE) + 1;
/* Do *not* flush the archive here. If we do, the same argument to
set_next_block_after could mean the next block (if the input record
idx_t
available_space_after (union block *pointer)
{
- return record_end->buffer - pointer->buffer;
+ return charptr (record_end) - charptr (pointer);
}
/* Close file having descriptor FD, and abort if close unsuccessful. */
short_read (idx_t status)
{
idx_t left = record_size - status; /* bytes left to read */
- char *more = (char *) record_start + status; /* address of next read */
+ char *more = charptr (record_start) + status; /* address of next read */
if (left && left % BLOCKSIZE == 0
&& (warning_option & WARN_RECORD_SIZE)
}
}
- buffer_level = current_block->buffer - record_start->buffer;
+ buffer_level = charptr (current_block) - charptr (record_start);
record_start_block += record_end - record_start;
current_block = record_start;
record_end = record_start + blocking_factor;
/* Seek back to the beginning of this record and start writing there. */
- position -= record_end->buffer - record_start->buffer;
+ position -= charptr (record_end) - charptr (record_start);
if (position < 0)
position = 0;
if (rmtlseek (archive, position, SEEK_SET) != position)
/* Replace the first part of the record with NULs. */
if (record_start->buffer != output_start)
- memset (record_start->buffer, 0,
- output_start - record_start->buffer);
+ memset (record_start, 0, output_start - charptr (record_start));
}
}
}
if (!new_volume (acc))
return true;
- while ((status = rmtread (archive, record_start->buffer, record_size))
+ while ((status = rmtread (archive, charptr (record_start), record_size))
< 0)
archive_read_error ();
}
ptrdiff_t nread;
- while ((nread = rmtread (archive, record_start->buffer, record_size)) < 0)
+ while ((nread = rmtread (archive, charptr (record_start), record_size)) < 0)
archive_read_error ();
short_read_slop = 0;
if (nread == record_size)
}
ptrdiff_t nread;
- while ((nread = rmtread (archive, record_start->buffer, record_size)) < 0
+ while ((nread = rmtread (archive, charptr (record_start), record_size)) < 0
&& ! (errno == ENOSPC && multi_volume_option))
archive_read_error ();
/* The condition below used to include
prev_written += bytes_written;
bytes_written = 0;
- copy_ptr = record_start->buffer + status;
+ copy_ptr = charptr (record_start) + status;
copy_size = buffer_level - status;
/* Switch to the next buffer */
inhibit_map = false;
while (bufsize < copy_size)
{
- memcpy (header->buffer, copy_ptr, bufsize);
+ memcpy (header, copy_ptr, bufsize);
copy_ptr += bufsize;
copy_size -= bufsize;
- set_next_block_after (header + ((bufsize - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (header) + bufsize - 1);
header = find_next_block ();
bufsize = available_space_after (header);
}
- memcpy (header->buffer, copy_ptr, copy_size);
- memset (header->buffer + copy_size, 0, bufsize - copy_size);
- set_next_block_after (header + ((copy_size - 1) >> LG_BLOCKSIZE));
+ memcpy (header, copy_ptr, copy_size);
+ memset (charptr (header) + copy_size, 0, bufsize - copy_size);
+ set_next_block_after (charptr (header) + copy_size - 1);
find_next_block ();
}
/* Module buffer.c. */
+/* Return BLOCK, but as a char * pointer that can be used to address
+ any byte in the array of blocks containing *BLOCK, as opposed to
+ BLOCK->buffer which can address only the bytes in *BLOCK itself.
+ The distinction can matter in strict debugging environments.
+
+ Callers should use this function only when possibly needing access
+ to storage outside of *BLOCK, or when the resulting pointer needs
+ to be compared to other pointers that point outside of *BLOCK.
+ Code not needing such access should use BLOCK->buffer instead, as
+ some debugging environments can catch some subscript errors that way. */
+COMMON_INLINE char *
+charptr (union block *block)
+{
+ return (char *) block;
+}
+
/* File descriptor for archive file. */
extern int archive;
void open_archive (enum access_mode mode);
void print_total_stats (void);
void reset_eof (void);
-void set_next_block_after (union block *block);
+void set_next_block_after (void *);
void clear_read_error_count (void);
void xclose (int fd);
_Noreturn void archive_write_error (ssize_t status);
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);
}
memset (pointer->buffer, 0, BLOCKSIZE);
set_next_block_after (pointer);
pointer = find_next_block ();
- memset (pointer->buffer, 0, available_space_after (pointer));
+ memset (charptr (pointer), 0, available_space_after (pointer));
set_next_block_after (pointer);
}
while (bufsize < size)
{
- memcpy (header->buffer, p, bufsize);
+ memcpy (charptr (header), p, bufsize);
p += bufsize;
size -= bufsize;
- set_next_block_after (header + ((bufsize - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (header) + bufsize - 1);
header = find_next_block ();
bufsize = available_space_after (header);
}
- memcpy (header->buffer, p, size);
- memset (header->buffer + size, 0, bufsize - size);
- set_next_block_after (header + ((size - 1) >> LG_BLOCKSIZE));
+ memcpy (charptr (header), p, size);
+ memset (charptr (header) + size, 0, bufsize - size);
+ set_next_block_after (charptr (header) + size - 1);
}
static int
}
idx_t count = (fd <= 0 ? bufsize
- : blocking_read (fd, blk->buffer, bufsize));
+ : blocking_read (fd, charptr (blk), bufsize));
size_left -= count;
- set_next_block_after (blk + ((bufsize - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (blk) + bufsize - 1);
if (count != bufsize)
{
if (errno)
read_diag_details (st->orig_file_name,
st->stat.st_size - size_left, bufsize);
- memset (blk->buffer + count, 0, bufsize - count);
+ memset (charptr (blk) + count, 0, bufsize - count);
warnopt (WARN_FILE_SHRANK, 0,
ngettext (("%s: File shrank by %jd byte;"
" padding with zeros"),
if (count)
memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
}
- memcpy (blk->buffer, p_buffer, bufsize);
+ memcpy (charptr (blk), p_buffer, bufsize);
size_left -= bufsize;
p_buffer += bufsize;
- set_next_block_after (blk + ((bufsize - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (blk) + bufsize - 1);
}
}
return;
if (written > size)
written = size;
errno = 0;
- idx_t count = blocking_write (fd, data_block->buffer, written);
+ idx_t count = blocking_write (fd, charptr (data_block), written);
size -= written;
- set_next_block_after ((union block *)
- (data_block->buffer + written - 1));
+ set_next_block_after (charptr (data_block) + written - 1);
if (count != written)
{
if (!to_command_option)
copied = available_space_after (data_block);
if (copied > size)
copied = size;
- memcpy (to, data_block->buffer, copied);
+ memcpy (to, charptr (data_block), copied);
to += copied;
- set_next_block_after ((union block *)
- (data_block->buffer + copied - 1));
+ set_next_block_after (charptr (data_block) + copied - 1);
}
mv_end ();
read_header_x_global when a POSIX global header is read,
decode it and return HEADER_SUCCESS_EXTENDED.
- You must always set_next_block_after(*return_block) to skip past
+ You must always set_next_block_after (*return_block) to skip past
the header which this routine reads. */
enum read_header
set_next_block_after (header);
*header_copy = *header;
- bp = header_copy->buffer + BLOCKSIZE;
+ bp = charptr (header_copy + 1);
for (size -= BLOCKSIZE; size > 0; size -= written)
{
if (written > size)
written = size;
- memcpy (bp, data_block->buffer, written);
+ memcpy (bp, charptr (data_block), written);
bp += written;
- set_next_block_after ((union block *)
- (data_block->buffer + written - 1));
+ set_next_block_after (charptr (data_block) + written - 1);
}
*bp = '\0';
if (next_long_name)
{
- name = next_long_name->buffer + BLOCKSIZE;
+ name = charptr (next_long_name + 1);
recent_long_name = next_long_name;
recent_long_name_blocks = next_long_name_blocks;
next_long_name = NULL;
if (next_long_link)
{
- name = next_long_link->buffer + BLOCKSIZE;
+ name = charptr (next_long_link + 1);
recent_long_link = next_long_link;
recent_long_link_blocks = next_long_link_blocks;
next_long_link = NULL;
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, blk->buffer, bufsize);
+ 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;
return false;
}
- set_next_block_after (blk + ((bufsize - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (blk) + bufsize - 1);
}
return true;
}
idx_t avail = available_space_after (blk);
idx_t wrbytes = min (write_size, avail);
- set_next_block_after (blk + ((wrbytes - 1) >> LG_BLOCKSIZE));
+ set_next_block_after (charptr (blk) + wrbytes - 1);
file->dumped_size += avail;
- idx_t count = blocking_write (file->fd, blk->buffer, wrbytes);
+ 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;
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. */
idx_t
sys_write_archive_buffer (void)
{
- return rmtwrite (archive, record_start->buffer, record_size);
+ return rmtwrite (archive, charptr (record_start), record_size);
}
/* Read and write file descriptors from a pipe(pipefd) call. */
/* Assemble a record. */
- for (length = 0, cursor = record_start->buffer;
+ for (length = 0, cursor = charptr (record_start);
length < record_size;
length += status, cursor += status)
{
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);
clear_read_error_count ();
ptrdiff_t n;
- while ((n = rmtread (archive, record_start->buffer, record_size)) < 0)
+ while ((n = rmtread (archive, charptr (record_start), record_size)) < 0)
archive_read_error ();
if (n == 0)
break;
- char *cursor = record_start->buffer;
+ char *cursor = charptr (record_start);
do
{
idx_t count = min (n, BLOCKSIZE);
{
union block *start = find_next_block ();
idx_t bufsize = available_space_after (start);
- idx_t status = full_read (handle, start->buffer, bufsize);
+ 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 (start->buffer + (status - rem), 0, BLOCKSIZE - rem);
- set_next_block_after (start + ((status - 1) >> LG_BLOCKSIZE));
+ memset (charptr (start) + (status - rem), 0, BLOCKSIZE - rem);
+ set_next_block_after (charptr (start) + status - 1);
}
if (close (handle) < 0)
reset_eof ();
time_to_start_writing = true;
- output_start = current_block->buffer;
+ output_start = charptr (current_block);
{
struct name const *p;