diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-02-27 13:28:40 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-02-27 13:28:40 +0000 |
commit | a783e2c18c6b566b2f4c2b06e6c388a9645c826c (patch) | |
tree | ae12abc157dee99bd4c67bab25316be06d3519a2 /src/copypass.c | |
parent | cb721df9888a296af824a4acab4916694d6dc35d (diff) | |
download | cpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.gz cpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.bz2 |
Added to the repository
Diffstat (limited to 'src/copypass.c')
-rw-r--r-- | src/copypass.c | 482 |
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 (×, 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, ×) < 0 + && errno != EROFS) + error (0, errno, "%s", input_name.ds_string); + if (utime (output_name.ds_string, ×) < 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, ×) < 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, ×) < 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; + 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; +} |