aboutsummaryrefslogtreecommitdiff
path: root/src/copypass.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2004-02-27 13:28:40 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2004-02-27 13:28:40 +0000
commita783e2c18c6b566b2f4c2b06e6c388a9645c826c (patch)
treeae12abc157dee99bd4c67bab25316be06d3519a2 /src/copypass.c
parentcb721df9888a296af824a4acab4916694d6dc35d (diff)
downloadcpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.gz
cpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.bz2
Added to the repository
Diffstat (limited to 'src/copypass.c')
-rw-r--r--src/copypass.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/src/copypass.c b/src/copypass.c
new file mode 100644
index 0000000..a852a3d
--- /dev/null
+++ b/src/copypass.c
@@ -0,0 +1,482 @@
+/* copypass.c - cpio copy pass sub-function.
+ Copyright (C) 1990, 1991, 1992, 2001, 2003 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.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#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 (&times, 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_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string);
+ disk_empty_output_buffer (out_file_des);
+ /* Debian hack to fix a bug in the --sparse option.
+ This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ if (delayed_seek_count > 0)
+ {
+ lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
+ write (out_file_des, "", 1);
+ delayed_seek_count = 0;
+ }
+ 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;
+ /* Debian hack: Silently ignore EROFS because
+ reading the file won't have upset its timestamp
+ if it's on a read-only filesystem. This has been
+ submitted as a suggestion to
+ "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ if (utime (input_name.ds_string, &times) < 0
+ && errno != EROFS)
+ error (0, errno, "%s", input_name.ds_string);
+ if (utime (output_name.ds_string, &times) < 0
+ && errno != EROFS)
+ 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, &times) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ }
+ warn_if_file_changed(input_name.ds_string, in_file_stat.st_size,
+ in_file_stat.st_mtime);
+ }
+ }
+ 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)
+ {
+ /* In some odd cases where the output_name includes `.',
+ the directory may have actually been created by
+ create_all_directories(), so the mkdir will fail
+ because the directory exists. If that's the case,
+ don't complain about it. */
+ if ( (errno != EEXIST) ||
+ (lstat (output_name.ds_string, &out_file_stat) != 0) ||
+ !(S_ISDIR (out_file_stat.st_mode) ) )
+ {
+ 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, &times) < 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, &times) < 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;
+ int link_size;
+ link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
+
+ link_size = readlink (input_name.ds_string, link_name,
+ in_file_stat.st_size);
+ if (link_size < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ free (link_name);
+ continue;
+ }
+ link_name[link_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);
+ if (!quiet_flag)
+ {
+ res = (output_bytes + io_block_size - 1) / io_block_size;
+ fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), 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;
+}

Return to:

Send suggestions and report system problems to the System administrator.