/* copyin.c - extract or list a cpio archive
Copyright (C) 1990-2024 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 "filetypes.h"
#include "cpiohdr.h"
#include "dstring.h"
#include "extern.h"
#include "defer.h"
#include <rmt.h>
#ifndef FNM_PATHNAME
# include <fnmatch.h>
#endif
#include <hash.h>
#ifndef HAVE_LCHOWN
# define lchown(f,u,g) 0
#endif
#include <timespec.h>
static void copyin_regular_file(struct cpio_file_stat* file_hdr,
int in_file_des);
void
warn_junk_bytes (long bytes_skipped)
{
error (0, 0, ngettext ("warning: skipped %ld byte of junk",
"warning: skipped %ld bytes of junk", bytes_skipped),
bytes_skipped);
}
static int
query_rename(struct cpio_file_stat* file_hdr, FILE *tty_in, FILE *tty_out,
FILE *rename_in)
{
char *str_res; /* Result for string function. */
static dynamic_string new_name; /* New file name for rename option. */
static int initialized_new_name = false;
if (!initialized_new_name)
{
ds_init (&new_name);
initialized_new_name = true;
}
if (rename_flag)
{
fprintf (tty_out, _("rename %s -> "), file_hdr->c_name);
fflush (tty_out);
str_res = ds_fgets (tty_in, &new_name);
}
else
{
str_res = ds_fgetstr (rename_in, &new_name, '\n');
}
if (str_res == NULL || str_res[0] == 0)
{
return -1;
}
else
cpio_set_c_name (file_hdr, new_name.ds_string);
return 0;
}
/* Skip the padding on IN_FILE_DES after a header or file,
up to the next header.
The number of bytes skipped is based on OFFSET -- the current offset
from the last start of a header (or file) -- and the current
header type. */
static void
tape_skip_padding (int in_file_des, off_t offset)
{
off_t pad;
if (archive_format == arf_crcascii || archive_format == arf_newascii)
pad = (4 - (offset % 4)) % 4;
else if (archive_format == arf_binary || archive_format == arf_hpbinary)
pad = (2 - (offset % 2)) % 2;
else if (archive_format == arf_tar || archive_format == arf_ustar)
pad = (512 - (offset % 512)) % 512;
else
pad = 0;
if (pad != 0)
tape_toss_input (in_file_des, pad);
}
static char *
get_link_name (struct cpio_file_stat *file_hdr, int in_file_des)
{
char *link_name;
if (file_hdr->c_filesize < 0 || file_hdr->c_filesize > SIZE_MAX-1)
{
error (0, 0, _("%s: stored filename length is out of range"),
file_hdr->c_name);
link_name = NULL;
}
else
{
link_name = xmalloc (file_hdr->c_filesize + 1);
tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
link_name[file_hdr->c_filesize] = '\0';
tape_skip_padding (in_file_des, file_hdr->c_filesize);
}
return link_name;
}
static void
list_file (struct cpio_file_stat* file_hdr, int in_file_des)
{
if (verbose_flag)
{
#ifdef CP_IFLNK
if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
{
if (archive_format != arf_tar && archive_format != arf_ustar)
{
char *link_name = get_link_name (file_hdr, in_file_des);
if (link_name)
{
long_format (file_hdr, link_name);
free (link_name);
}
}
else
long_format (file_hdr, file_hdr->c_tar_linkname);
return;
}
else
#endif
long_format (file_hdr, (char *) 0);
}
else
{
/* Print out the name as it is. The name_end delimiter is normally
'\n', but can be reset to '\0' by the -0 option. */
printf ("%s%c", file_hdr->c_name, name_end);
}
crc = 0;
tape_toss_input (in_file_des, file_hdr->c_filesize);
tape_skip_padding (in_file_des, file_hdr->c_filesize);
if (only_verify_crc_flag)
{
#ifdef CP_IFLNK
if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
{
return; /* links don't have a checksum */
}
#endif
if (crc != file_hdr->c_chksum)
{
error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
file_hdr->c_name, crc, file_hdr->c_chksum);
}
}
}
static int
try_existing_file (struct cpio_file_stat* file_hdr, int in_file_des,
bool *existing_dir)
{
struct stat file_stat;
*existing_dir = false;
if (lstat (file_hdr->c_name, &file_stat) == 0)
{
if (S_ISDIR (file_stat.st_mode)
&& ((file_hdr->c_mode & CP_IFMT) == CP_IFDIR))
{
/* If there is already a directory there that
we are trying to create, don't complain about
it. */
*existing_dir = true;
return 0;
}
else if (!unconditional_flag
&& file_hdr->c_mtime <= file_stat.st_mtime)
{
error (0, 0, _("%s not created: newer or same age version exists"),
file_hdr->c_name);
tape_toss_input (in_file_des, file_hdr->c_filesize);
tape_skip_padding (in_file_des, file_hdr->c_filesize);
return -1; /* Go to the next file. */
}
else if (S_ISDIR (file_stat.st_mode)
? rmdir (file_hdr->c_name)
: unlink (file_hdr->c_name))
{
error (0, errno, _("cannot remove current %s"),
file_hdr->c_name);
tape_toss_input (in_file_des, file_hdr->c_filesize);
tape_skip_padding (in_file_des, file_hdr->c_filesize);
return -1; /* Go to the next file. */
}
}
return 0;
}
/* The newc and crc formats store multiply linked copies of the same file
in the archive only once. The actual data is attached to the last link
in the archive, and the other links all have a filesize of 0. When a
file in the archive has multiple links and a filesize of 0, its data is
probably "attatched" to another file in the archive, so we can't create
it right away. We have to "defer" creating it until we have created
the file that h
|