/* util.c - Several utility routines for cpio.
Copyright (C) 1990-1992, 2001, 2004, 2006-2007, 2010-2011, 2014-2015,
2017, 2020-2021 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA. */
#include <system.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "cpiohdr.h"
#include "dstring.h"
#include "extern.h"
#include <paxlib.h>
#include "filetypes.h"
#include <safe-read.h>
#include <full-write.h>
#include <rmt.h>
#include <hash.h>
#include <utimens.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_MTIO_H
# ifdef HAVE_SYS_IO_TRIOCTL_H
# include <sys/io/trioctl.h>
# endif
# include <sys/mtio.h>
#endif
#if !HAVE_DECL_ERRNO
extern int errno;
#endif
/* Write `output_size' bytes of `output_buffer' to file
descriptor OUT_DES and reset `output_size' and `out_buff'. */
void
tape_empty_output_buffer (int out_des)
{
int bytes_written;
#ifdef BROKEN_LONG_TAPE_DRIVER
static long output_bytes_before_lseek = 0;
/* Some tape drivers seem to have a signed internal seek pointer and
they lose if it overflows and becomes negative (e.g. when writing
tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
seek pointer and prevent it from overflowing. */
if (output_is_special
&& ( (output_bytes_before_lseek += output_size) >= 1073741824L) )
{
lseek(out_des, 0L, SEEK_SET);
output_bytes_before_lseek = 0;
}
#endif
bytes_written = rmtwrite (out_des, output_buffer, output_size);
if (bytes_written != output_size)
{
int rest_bytes_written;
int rest_output_size;
if (output_is_special
&& (bytes_written >= 0
|| (bytes_written < 0
&& (errno == ENOSPC || errno == EIO || errno == ENXIO))))
{
get_next_reel (out_des);
if (bytes_written > 0)
rest_output_size = output_size - bytes_written;
else
rest_output_size = output_size;
rest_bytes_written = rmtwrite (out_des, output_buffer,
rest_output_size);
if (rest_bytes_written != rest_output_size)
error (PAXEXIT_FAILURE, errno, _("write error"));
}
else
error (PAXEXIT_FAILURE, errno, _("write error"));
}
output_bytes += output_size;
out_buff = output_buffer;
output_size = 0;
}
static ssize_t sparse_write (int fildes, char *buf, size_t nbyte, bool flush);
/* Write `output_size' bytes of `output_buffer' to file
descriptor OUT_DES and reset `output_size' and `out_buff'.
If `swapping_halfwords' or `swapping_bytes' is set,
do the appropriate swapping first. Our callers have
to make sure to only set these flags if `output_size'
is appropriate (a multiple of 4 for `swapping_halfwords',
2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE
must always be a multiple of 4 helps us (and our callers)
insure this. */
void
disk_empty_output_buffer (int out_des, bool flush)
{
ssize_t bytes_written;
if (swapping_halfwords || swapping_bytes)
{
if (swapping_halfwords)
{
int complete_words;
complete_words = output_size / 4;
swahw_array (output_buffer, complete_words);
if (swapping_bytes)
swab_array (output_buffer, 2 * complete_words);
}
else
{
int complete_halfwords;
complete_halfwords = output_size /2;
swab_array (output_buffer, complete_halfwords);
}
}
if (sparse_flag)
bytes_written = sparse_write (out_des, output_buffer, output_size, flush);
else
bytes_written = write (out_des, output_buffer, output_size);
if (bytes_written != output_size)
{
if (bytes_written == -1)
error (PAXEXIT_FAILURE, errno, _("write error"));
else
error (PAXEXIT_FAILURE, 0, _("write error: partial write"));
}
output_bytes += output_size;
out_buff = output_buffer;
output_size = 0;
}
/* Exchange the halfwords of each element of the array of COUNT longs
starting at PTR. PTR does not have to be aligned at a word
boundary. */
void
swahw_array (char *ptr, int count)
{
char tmp;
for (; count > 0; --count)
{
tmp = *ptr;
*ptr = *(ptr + 2);
*(ptr + 2) = tmp;
++ptr;
tmp = *ptr;
*ptr = *(ptr + 2);
*(ptr + 2) = tmp;
ptr += 3;
}
}
/* Read at most NUM_BYTES or `io_block_size' bytes, whichever is smaller,
into the start of `input_buffer' from file descriptor IN_DES.
Set `input_size' to the number of bytes read and reset `in_buff'.
Exit with an error if end of file is reached. */
#ifdef BROKEN_LONG_TAPE_DRIVER
static long input_bytes_before_lseek = 0;
#endif
static void
tape_fill_input_buffer (int in_des, int num_bytes)
{
#ifdef BROKEN_LONG_TAPE_DRIVER
/* Some tape drivers seem to have a signed internal seek pointer and
they lose if it overflows and becomes negative (e.g. when writing
tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
seek pointer and prevent it from overflowing. */
if (input_is_special
&& ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
{
lseek(in_des, 0L, SEEK_SET);
input_bytes_before_lseek = 0;
}
#endif
in_buff = input_buffer;
num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size;
input_size = rmtread (in_des, input_buffer, num_bytes);
if (input_size == 0 && input_is_special)
{
get_next_reel (in_des);
input_size = rmtread (in_des, input_buffer, num_bytes);
}
if (input_size == SAFE_READ_ERROR)
error (PAXEXIT_FAILURE, errno, _("read error"));
if (input_size == 0)
error (PAXEXIT_FAILURE, 0, _("premature end of file"));
input_bytes += input_size;
}
/* Read at most NUM_BYTES or `DISK_IO_BLOCK_SIZE' bytes, whichever is smaller,
into the start of `input_buffer' from file descriptor IN_DES.
Set `input_size' to the number of bytes read and reset `in_buff'.
Exit with an error if end of file is reached. */
static int
disk_fill_input_buffer (int in_des, off_t num_bytes)
{
in_buff = input_buffer;
num_bytes = (num_bytes < DISK_IO_BLOCK_SIZE) ? num_bytes : DISK_IO_BLOCK_SIZE;
input_size = read (in_des, input_buffer, num_bytes);
if (input_size == SAFE_READ_ERROR)
{
input_size = 0;
return (-1);
}
else if (input_size == 0)
return (1);
input_bytes += input_size;
return (0);
}
/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
When `out_buff' fills up, flush it to file descriptor OUT_DES. */
void
tape_buffered_write (char *in_buf, int out_des, off_t num_bytes)
{
off_t bytes_left = num_bytes; /* Bytes needing to be copied. */
off_t space_left; /* Room left in output buffer. */
while (bytes_left > 0)
{
space_left = io_block_size - output_size;
if (space_left == 0)
tape_empty_output_buffer (out_des);
else
{
if (bytes_left < space_left)
space_left = bytes_left;
memcpy (out_buff, in_buf, (unsigned) space_left);
out_buff += space_left;
output_size += space_left;
in_buf += space_left;
bytes_left -= space_left;
}
}
}
/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
When `out_buff' fills up, flush it to file descriptor OUT_DES. */
void
disk_buffered_write (char *in_buf, int out_des, off_t num_bytes)
{
off_t bytes_left = num_bytes; /* Bytes needing to be copied. */
off_t space_left; /* Room left in output buffer. */
while (bytes_left > 0)
{
space_left = DISK_IO_BLOCK_SIZE - output_size;
if (space_left == 0)
disk_empty_output_buffer (out_des, false);
else
{
if (byte
|