450 lines
14 KiB
C
450 lines
14 KiB
C
|
/* copypass.c - cpio copy pass sub-function.
|
|||
|
Copyright (C) 1990, 1991, 1992 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
#include <sys/types.h>
|
|||
|
#include <sys/stat.h>
|
|||
|
#include "filetypes.h"
|
|||
|
#include "system.h"
|
|||
|
#include "cpiohdr.h"
|
|||
|
#include "dstring.h"
|
|||
|
#include "extern.h"
|
|||
|
|
|||
|
#ifndef HAVE_LCHOWN
|
|||
|
#define lchown chown
|
|||
|
#endif
|
|||
|
|
|||
|
/* Copy files listed on the standard input into directory `directory_name'.
|
|||
|
If `link_flag', link instead of copying. */
|
|||
|
|
|||
|
void
|
|||
|
process_copy_pass ()
|
|||
|
{
|
|||
|
dynamic_string input_name; /* Name of file from stdin. */
|
|||
|
dynamic_string output_name; /* Name of new file. */
|
|||
|
int dirname_len; /* Length of `directory_name'. */
|
|||
|
int res; /* Result of functions. */
|
|||
|
char *slash; /* For moving past slashes in input name. */
|
|||
|
struct utimbuf times; /* For resetting file times after copy. */
|
|||
|
struct stat in_file_stat; /* Stat record for input file. */
|
|||
|
struct stat out_file_stat; /* Stat record for output file. */
|
|||
|
int in_file_des; /* Input file descriptor. */
|
|||
|
int out_file_des; /* Output file descriptor. */
|
|||
|
int existing_dir; /* True if file is a dir & already exists. */
|
|||
|
#ifdef HPUX_CDF
|
|||
|
int cdf_flag;
|
|||
|
int cdf_char;
|
|||
|
#endif
|
|||
|
|
|||
|
/* Initialize the copy pass. */
|
|||
|
dirname_len = strlen (directory_name);
|
|||
|
ds_init (&input_name, 128);
|
|||
|
ds_init (&output_name, dirname_len + 2);
|
|||
|
strcpy (output_name.ds_string, directory_name);
|
|||
|
output_name.ds_string[dirname_len] = '/';
|
|||
|
output_is_seekable = TRUE;
|
|||
|
/* Initialize this in case it has members we don't know to set. */
|
|||
|
bzero (×, sizeof (struct utimbuf));
|
|||
|
|
|||
|
/* Copy files with names read from stdin. */
|
|||
|
while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
|
|||
|
{
|
|||
|
int link_res = -1;
|
|||
|
|
|||
|
/* Check for blank line and ignore it if found. */
|
|||
|
if (input_name.ds_string[0] == '\0')
|
|||
|
{
|
|||
|
error (0, 0, "blank line ignored");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* Check for current directory and ignore it if found. */
|
|||
|
if (input_name.ds_string[0] == '.'
|
|||
|
&& (input_name.ds_string[1] == '\0'
|
|||
|
|| (input_name.ds_string[1] == '/'
|
|||
|
&& input_name.ds_string[2] == '\0')))
|
|||
|
continue;
|
|||
|
|
|||
|
if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", input_name.ds_string);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* Make the name of the new file. */
|
|||
|
for (slash = input_name.ds_string; *slash == '/'; ++slash)
|
|||
|
;
|
|||
|
#ifdef HPUX_CDF
|
|||
|
/* For CDF's we add a 2nd `/' after all "hidden" directories.
|
|||
|
This kind of a kludge, but it's what we do when creating
|
|||
|
archives, and it's easier to do this than to separately
|
|||
|
keep track of which directories in a path are "hidden". */
|
|||
|
slash = add_cdf_double_slashes (slash);
|
|||
|
#endif
|
|||
|
ds_resize (&output_name, dirname_len + strlen (slash) + 2);
|
|||
|
strcpy (output_name.ds_string + dirname_len + 1, slash);
|
|||
|
|
|||
|
existing_dir = FALSE;
|
|||
|
if (lstat (output_name.ds_string, &out_file_stat) == 0)
|
|||
|
{
|
|||
|
if (S_ISDIR (out_file_stat.st_mode)
|
|||
|
&& S_ISDIR (in_file_stat.st_mode))
|
|||
|
{
|
|||
|
/* If there is already a directory there that
|
|||
|
we are trying to create, don't complain about it. */
|
|||
|
existing_dir = TRUE;
|
|||
|
}
|
|||
|
else if (!unconditional_flag
|
|||
|
&& in_file_stat.st_mtime <= out_file_stat.st_mtime)
|
|||
|
{
|
|||
|
error (0, 0, "%s not created: newer or same age version exists",
|
|||
|
output_name.ds_string);
|
|||
|
continue; /* Go to the next file. */
|
|||
|
}
|
|||
|
else if (S_ISDIR (out_file_stat.st_mode)
|
|||
|
? rmdir (output_name.ds_string)
|
|||
|
: unlink (output_name.ds_string))
|
|||
|
{
|
|||
|
error (0, errno, "cannot remove current %s",
|
|||
|
output_name.ds_string);
|
|||
|
continue; /* Go to the next file. */
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Do the real copy or link. */
|
|||
|
if (S_ISREG (in_file_stat.st_mode))
|
|||
|
{
|
|||
|
#ifndef __MSDOS__
|
|||
|
/* Can the current file be linked to a another file?
|
|||
|
Set link_name to the original file name. */
|
|||
|
if (link_flag)
|
|||
|
/* User said to link it if possible. Try and link to
|
|||
|
the original copy. If that fails we'll still try
|
|||
|
and link to a copy we've already made. */
|
|||
|
link_res = link_to_name (output_name.ds_string,
|
|||
|
input_name.ds_string);
|
|||
|
if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
|
|||
|
link_res = link_to_maj_min_ino (output_name.ds_string,
|
|||
|
major (in_file_stat.st_dev),
|
|||
|
minor (in_file_stat.st_dev),
|
|||
|
in_file_stat.st_ino);
|
|||
|
#endif
|
|||
|
|
|||
|
/* If the file was not linked, copy contents of file. */
|
|||
|
if (link_res < 0)
|
|||
|
{
|
|||
|
in_file_des = open (input_name.ds_string,
|
|||
|
O_RDONLY | O_BINARY, 0);
|
|||
|
if (in_file_des < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", input_name.ds_string);
|
|||
|
continue;
|
|||
|
}
|
|||
|
out_file_des = open (output_name.ds_string,
|
|||
|
O_CREAT | O_WRONLY | O_BINARY, 0600);
|
|||
|
if (out_file_des < 0 && create_dir_flag)
|
|||
|
{
|
|||
|
create_all_directories (output_name.ds_string);
|
|||
|
out_file_des = open (output_name.ds_string,
|
|||
|
O_CREAT | O_WRONLY | O_BINARY, 0600);
|
|||
|
}
|
|||
|
if (out_file_des < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
close (in_file_des);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
copy_files (in_file_des, out_file_des, in_file_stat.st_size);
|
|||
|
empty_output_buffer (out_file_des);
|
|||
|
if (close (in_file_des) < 0)
|
|||
|
error (0, errno, "%s", input_name.ds_string);
|
|||
|
if (close (out_file_des) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
|
|||
|
/* Set the attributes of the new file. */
|
|||
|
if (!no_chown_flag)
|
|||
|
if ((chown (output_name.ds_string,
|
|||
|
set_owner_flag ? set_owner : in_file_stat.st_uid,
|
|||
|
set_group_flag ? set_group : in_file_stat.st_gid) < 0)
|
|||
|
&& errno != EPERM)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
/* chown may have turned off some permissions we wanted. */
|
|||
|
if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
if (reset_time_flag)
|
|||
|
{
|
|||
|
times.actime = in_file_stat.st_atime;
|
|||
|
times.modtime = in_file_stat.st_mtime;
|
|||
|
if (utime (input_name.ds_string, ×) < 0)
|
|||
|
error (0, errno, "%s", input_name.ds_string);
|
|||
|
if (utime (output_name.ds_string, ×) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
}
|
|||
|
if (retain_time_flag)
|
|||
|
{
|
|||
|
times.actime = times.modtime = in_file_stat.st_mtime;
|
|||
|
if (utime (output_name.ds_string, ×) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else if (S_ISDIR (in_file_stat.st_mode))
|
|||
|
{
|
|||
|
#ifdef HPUX_CDF
|
|||
|
cdf_flag = 0;
|
|||
|
#endif
|
|||
|
if (!existing_dir)
|
|||
|
{
|
|||
|
#ifdef HPUX_CDF
|
|||
|
/* If the directory name ends in a + and is SUID,
|
|||
|
then it is a CDF. Strip the trailing + from the name
|
|||
|
before creating it. */
|
|||
|
cdf_char = strlen (output_name.ds_string) - 1;
|
|||
|
if ( (cdf_char > 0) &&
|
|||
|
(in_file_stat.st_mode & 04000) &&
|
|||
|
(output_name.ds_string [cdf_char] == '+') )
|
|||
|
{
|
|||
|
output_name.ds_string [cdf_char] = '\0';
|
|||
|
cdf_flag = 1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
res = mkdir (output_name.ds_string, in_file_stat.st_mode);
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
res = 0;
|
|||
|
if (res < 0 && create_dir_flag)
|
|||
|
{
|
|||
|
create_all_directories (output_name.ds_string);
|
|||
|
res = mkdir (output_name.ds_string, in_file_stat.st_mode);
|
|||
|
}
|
|||
|
if (res < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (!no_chown_flag)
|
|||
|
if ((chown (output_name.ds_string,
|
|||
|
set_owner_flag ? set_owner : in_file_stat.st_uid,
|
|||
|
set_group_flag ? set_group : in_file_stat.st_gid) < 0)
|
|||
|
&& errno != EPERM)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
/* chown may have turned off some permissions we wanted. */
|
|||
|
if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
#ifdef HPUX_CDF
|
|||
|
if (cdf_flag)
|
|||
|
/* Once we "hide" the directory with the chmod(),
|
|||
|
we have to refer to it using name+ isntead of name. */
|
|||
|
output_name.ds_string [cdf_char] = '+';
|
|||
|
#endif
|
|||
|
if (retain_time_flag)
|
|||
|
{
|
|||
|
times.actime = times.modtime = in_file_stat.st_mtime;
|
|||
|
if (utime (output_name.ds_string, ×) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
}
|
|||
|
}
|
|||
|
#ifndef __MSDOS__
|
|||
|
else if (S_ISCHR (in_file_stat.st_mode) ||
|
|||
|
S_ISBLK (in_file_stat.st_mode) ||
|
|||
|
#ifdef S_ISFIFO
|
|||
|
S_ISFIFO (in_file_stat.st_mode) ||
|
|||
|
#endif
|
|||
|
#ifdef S_ISSOCK
|
|||
|
S_ISSOCK (in_file_stat.st_mode) ||
|
|||
|
#endif
|
|||
|
0)
|
|||
|
{
|
|||
|
/* Can the current file be linked to a another file?
|
|||
|
Set link_name to the original file name. */
|
|||
|
if (link_flag)
|
|||
|
/* User said to link it if possible. */
|
|||
|
link_res = link_to_name (output_name.ds_string,
|
|||
|
input_name.ds_string);
|
|||
|
if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
|
|||
|
link_res = link_to_maj_min_ino (output_name.ds_string,
|
|||
|
major (in_file_stat.st_dev),
|
|||
|
minor (in_file_stat.st_dev),
|
|||
|
in_file_stat.st_ino);
|
|||
|
|
|||
|
if (link_res < 0)
|
|||
|
{
|
|||
|
res = mknod (output_name.ds_string, in_file_stat.st_mode,
|
|||
|
in_file_stat.st_rdev);
|
|||
|
if (res < 0 && create_dir_flag)
|
|||
|
{
|
|||
|
create_all_directories (output_name.ds_string);
|
|||
|
res = mknod (output_name.ds_string, in_file_stat.st_mode,
|
|||
|
in_file_stat.st_rdev);
|
|||
|
}
|
|||
|
if (res < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (!no_chown_flag)
|
|||
|
if ((chown (output_name.ds_string,
|
|||
|
set_owner_flag ? set_owner : in_file_stat.st_uid,
|
|||
|
set_group_flag ? set_group : in_file_stat.st_gid) < 0)
|
|||
|
&& errno != EPERM)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
/* chown may have turned off some permissions we wanted. */
|
|||
|
if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
if (retain_time_flag)
|
|||
|
{
|
|||
|
times.actime = times.modtime = in_file_stat.st_mtime;
|
|||
|
if (utime (output_name.ds_string, ×) < 0)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef S_ISLNK
|
|||
|
else if (S_ISLNK (in_file_stat.st_mode))
|
|||
|
{
|
|||
|
char *link_name;
|
|||
|
link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
|
|||
|
|
|||
|
if (readlink (input_name.ds_string, link_name,
|
|||
|
in_file_stat.st_size) < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", input_name.ds_string);
|
|||
|
free (link_name);
|
|||
|
continue;
|
|||
|
}
|
|||
|
link_name[in_file_stat.st_size] = '\0';
|
|||
|
|
|||
|
res = UMASKED_SYMLINK (link_name, output_name.ds_string,
|
|||
|
in_file_stat.st_mode);
|
|||
|
if (res < 0 && create_dir_flag)
|
|||
|
{
|
|||
|
create_all_directories (output_name.ds_string);
|
|||
|
res = UMASKED_SYMLINK (link_name, output_name.ds_string,
|
|||
|
in_file_stat.st_mode);
|
|||
|
}
|
|||
|
if (res < 0)
|
|||
|
{
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
free (link_name);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* Set the attributes of the new link. */
|
|||
|
if (!no_chown_flag)
|
|||
|
if ((lchown (output_name.ds_string,
|
|||
|
set_owner_flag ? set_owner : in_file_stat.st_uid,
|
|||
|
set_group_flag ? set_group : in_file_stat.st_gid) < 0)
|
|||
|
&& errno != EPERM)
|
|||
|
error (0, errno, "%s", output_name.ds_string);
|
|||
|
free (link_name);
|
|||
|
}
|
|||
|
#endif
|
|||
|
else
|
|||
|
{
|
|||
|
error (0, 0, "%s: unknown file type", input_name.ds_string);
|
|||
|
}
|
|||
|
|
|||
|
if (verbose_flag)
|
|||
|
fprintf (stderr, "%s\n", output_name.ds_string);
|
|||
|
if (dot_flag)
|
|||
|
fputc ('.', stderr);
|
|||
|
}
|
|||
|
|
|||
|
if (dot_flag)
|
|||
|
fputc ('\n', stderr);
|
|||
|
res = (output_bytes + io_block_size - 1) / io_block_size;
|
|||
|
if (res == 1)
|
|||
|
fprintf (stderr, "1 block\n");
|
|||
|
else
|
|||
|
fprintf (stderr, "%d blocks\n", res);
|
|||
|
}
|
|||
|
|
|||
|
/* Try and create a hard link from FILE_NAME to another file
|
|||
|
with the given major/minor device number and inode. If no other
|
|||
|
file with the same major/minor/inode numbers is known, add this file
|
|||
|
to the list of known files and associated major/minor/inode numbers
|
|||
|
and return -1. If another file with the same major/minor/inode
|
|||
|
numbers is found, try and create another link to it using
|
|||
|
link_to_name, and return 0 for success and -1 for failure. */
|
|||
|
|
|||
|
int
|
|||
|
link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino)
|
|||
|
char *file_name;
|
|||
|
int st_dev_maj;
|
|||
|
int st_dev_min;
|
|||
|
int st_ino;
|
|||
|
{
|
|||
|
int link_res;
|
|||
|
char *link_name;
|
|||
|
link_res = -1;
|
|||
|
#ifndef __MSDOS__
|
|||
|
/* Is the file a link to a previously copied file? */
|
|||
|
link_name = find_inode_file (st_ino,
|
|||
|
st_dev_maj,
|
|||
|
st_dev_min);
|
|||
|
if (link_name == NULL)
|
|||
|
add_inode (st_ino, file_name,
|
|||
|
st_dev_maj,
|
|||
|
st_dev_min);
|
|||
|
else
|
|||
|
link_res = link_to_name (file_name, link_name);
|
|||
|
#endif
|
|||
|
return link_res;
|
|||
|
}
|
|||
|
|
|||
|
/* Try and create a hard link from LINK_NAME to LINK_TARGET. If
|
|||
|
`create_dir_flag' is set, any non-existent (parent) directories
|
|||
|
needed by LINK_NAME will be created. If the link is successfully
|
|||
|
created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
|
|||
|
If the link can not be created and `link_flag' is set, print
|
|||
|
"cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
|
|||
|
is created, -1 otherwise. */
|
|||
|
|
|||
|
int
|
|||
|
link_to_name (link_name, link_target)
|
|||
|
char *link_name;
|
|||
|
char *link_target;
|
|||
|
{
|
|||
|
int res;
|
|||
|
#ifdef __MSDOS__
|
|||
|
res = -1;
|
|||
|
#else /* not __MSDOS__ */
|
|||
|
res = link (link_target, link_name);
|
|||
|
if (res < 0 && create_dir_flag)
|
|||
|
{
|
|||
|
create_all_directories (link_name);
|
|||
|
res = link (link_target, link_name);
|
|||
|
}
|
|||
|
if (res == 0)
|
|||
|
{
|
|||
|
if (verbose_flag)
|
|||
|
error (0, 0, "%s linked to %s",
|
|||
|
link_target, link_name);
|
|||
|
}
|
|||
|
else if (link_flag)
|
|||
|
{
|
|||
|
error (0, errno, "cannot link %s to %s",
|
|||
|
link_target, link_name);
|
|||
|
}
|
|||
|
#endif /* not __MSDOS__ */
|
|||
|
return res;
|
|||
|
}
|