3 /* copypass.c - cpio copy pass sub-function.
4 Copyright (C) 1990, 1991, 1992, 2001, 2003, 2004,
5 2006 Free Software Foundation, Inc.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the Free
19 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301 USA. */
25 #include <sys/types.h>
27 #include "filetypes.h"
38 /* A wrapper around set_perms using another set of arguments */
40 set_copypass_perms (int fd, const char *name, struct stat *st)
42 struct cpio_file_stat header;
44 stat_to_cpio (&header, st);
45 set_perms (fd, &header);
48 /* Copy files listed on the standard input into directory `directory_name'.
49 If `link_flag', link instead of copying. */
54 dynamic_string input_name; /* Name of file from stdin. */
55 dynamic_string output_name; /* Name of new file. */
56 int dirname_len; /* Length of `directory_name'. */
57 int res; /* Result of functions. */
58 char *slash; /* For moving past slashes in input name. */
59 struct stat in_file_stat; /* Stat record for input file. */
60 struct stat out_file_stat; /* Stat record for output file. */
61 int in_file_des; /* Input file descriptor. */
62 int out_file_des; /* Output file descriptor. */
63 int existing_dir; /* True if file is a dir & already exists. */
69 umask (0); /* Reset umask to preserve modes of
72 /* Initialize the copy pass. */
73 dirname_len = strlen (directory_name);
74 ds_init (&input_name, 128);
75 ds_init (&output_name, dirname_len + 2);
76 strcpy (output_name.ds_string, directory_name);
77 output_name.ds_string[dirname_len] = '/';
78 output_is_seekable = true;
80 /* Copy files with names read from stdin. */
81 while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
85 /* Check for blank line and ignore it if found. */
86 if (input_name.ds_string[0] == '\0')
88 error (0, 0, _("blank line ignored"));
92 /* Check for current directory and ignore it if found. */
93 if (input_name.ds_string[0] == '.'
94 && (input_name.ds_string[1] == '\0'
95 || (input_name.ds_string[1] == '/'
96 && input_name.ds_string[2] == '\0')))
99 if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
101 stat_error (input_name.ds_string);
105 /* Make the name of the new file. */
106 for (slash = input_name.ds_string; *slash == '/'; ++slash)
109 /* For CDF's we add a 2nd `/' after all "hidden" directories.
110 This kind of a kludge, but it's what we do when creating
111 archives, and it's easier to do this than to separately
112 keep track of which directories in a path are "hidden". */
113 slash = add_cdf_double_slashes (slash);
115 ds_resize (&output_name, dirname_len + strlen (slash) + 2);
116 strcpy (output_name.ds_string + dirname_len + 1, slash);
118 existing_dir = false;
119 if (lstat (output_name.ds_string, &out_file_stat) == 0)
121 if (S_ISDIR (out_file_stat.st_mode)
122 && S_ISDIR (in_file_stat.st_mode))
124 /* If there is already a directory there that
125 we are trying to create, don't complain about it. */
128 else if (!unconditional_flag
129 && in_file_stat.st_mtime <= out_file_stat.st_mtime)
131 error (0, 0, _("%s not created: newer or same age version exists"),
132 output_name.ds_string);
133 continue; /* Go to the next file. */
135 else if (S_ISDIR (out_file_stat.st_mode)
136 ? rmdir (output_name.ds_string)
137 : unlink (output_name.ds_string))
139 error (0, errno, _("cannot remove current %s"),
140 output_name.ds_string);
141 continue; /* Go to the next file. */
145 /* Do the real copy or link. */
146 if (S_ISREG (in_file_stat.st_mode))
148 /* Can the current file be linked to a another file?
149 Set link_name to the original file name. */
151 /* User said to link it if possible. Try and link to
152 the original copy. If that fails we'll still try
153 and link to a copy we've already made. */
154 link_res = link_to_name (output_name.ds_string,
155 input_name.ds_string);
156 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
157 link_res = link_to_maj_min_ino (output_name.ds_string,
158 major (in_file_stat.st_dev),
159 minor (in_file_stat.st_dev),
160 in_file_stat.st_ino);
162 /* If the file was not linked, copy contents of file. */
165 in_file_des = open (input_name.ds_string,
166 O_RDONLY | O_BINARY, 0);
169 open_error (input_name.ds_string);
172 out_file_des = open (output_name.ds_string,
173 O_CREAT | O_WRONLY | O_BINARY, 0600);
174 if (out_file_des < 0 && create_dir_flag)
176 create_all_directories (output_name.ds_string);
177 out_file_des = open (output_name.ds_string,
178 O_CREAT | O_WRONLY | O_BINARY, 0600);
180 if (out_file_des < 0)
182 open_error (output_name.ds_string);
187 copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string);
188 disk_empty_output_buffer (out_file_des);
189 /* Debian hack to fix a bug in the --sparse option.
190 This bug has been reported to
191 "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
192 if (delayed_seek_count > 0)
194 lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
195 write (out_file_des, "", 1);
196 delayed_seek_count = 0;
199 set_copypass_perms (out_file_des,
200 output_name.ds_string, &in_file_stat);
204 set_file_times (in_file_des,
205 input_name.ds_string,
206 in_file_stat.st_atime,
207 in_file_stat.st_mtime);
208 set_file_times (out_file_des,
209 output_name.ds_string,
210 in_file_stat.st_atime,
211 in_file_stat.st_mtime);
214 if (close (in_file_des) < 0)
215 close_error (input_name.ds_string);
217 if (close (out_file_des) < 0)
218 close_error (output_name.ds_string);
220 warn_if_file_changed(input_name.ds_string, in_file_stat.st_size,
221 in_file_stat.st_mtime);
224 else if (S_ISDIR (in_file_stat.st_mode))
232 /* If the directory name ends in a + and is SUID,
233 then it is a CDF. Strip the trailing + from the name
234 before creating it. */
235 cdf_char = strlen (output_name.ds_string) - 1;
236 if ( (cdf_char > 0) &&
237 (in_file_stat.st_mode & 04000) &&
238 (output_name.ds_string [cdf_char] == '+') )
240 output_name.ds_string [cdf_char] = '\0';
244 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
249 if (res < 0 && create_dir_flag)
251 create_all_directories (output_name.ds_string);
252 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
256 /* In some odd cases where the output_name includes `.',
257 the directory may have actually been created by
258 create_all_directories(), so the mkdir will fail
259 because the directory exists. If that's the case,
260 don't complain about it. */
261 if ( (errno != EEXIST) ||
262 (lstat (output_name.ds_string, &out_file_stat) != 0) ||
263 !(S_ISDIR (out_file_stat.st_mode) ) )
265 stat_error (output_name.ds_string);
269 set_copypass_perms (-1, output_name.ds_string, &in_file_stat);
271 else if (S_ISCHR (in_file_stat.st_mode) ||
272 S_ISBLK (in_file_stat.st_mode) ||
274 S_ISFIFO (in_file_stat.st_mode) ||
277 S_ISSOCK (in_file_stat.st_mode) ||
281 /* Can the current file be linked to a another file?
282 Set link_name to the original file name. */
284 /* User said to link it if possible. */
285 link_res = link_to_name (output_name.ds_string,
286 input_name.ds_string);
287 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
288 link_res = link_to_maj_min_ino (output_name.ds_string,
289 major (in_file_stat.st_dev),
290 minor (in_file_stat.st_dev),
291 in_file_stat.st_ino);
296 if (S_ISFIFO (in_file_stat.st_mode))
297 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
300 res = mknod (output_name.ds_string, in_file_stat.st_mode,
301 in_file_stat.st_rdev);
302 if (res < 0 && create_dir_flag)
304 create_all_directories (output_name.ds_string);
306 if (S_ISFIFO (in_file_stat.st_mode))
307 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
310 res = mknod (output_name.ds_string, in_file_stat.st_mode,
311 in_file_stat.st_rdev);
315 mknod_error (output_name.ds_string);
318 set_copypass_perms (-1, output_name.ds_string, &in_file_stat);
323 else if (S_ISLNK (in_file_stat.st_mode))
327 link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
329 link_size = readlink (input_name.ds_string, link_name,
330 in_file_stat.st_size);
333 readlink_error (input_name.ds_string);
337 link_name[link_size] = '\0';
339 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
340 in_file_stat.st_mode);
341 if (res < 0 && create_dir_flag)
343 create_all_directories (output_name.ds_string);
344 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
345 in_file_stat.st_mode);
349 symlink_error (output_name.ds_string, link_name);
354 /* Set the attributes of the new link. */
357 uid_t uid = set_owner_flag ? set_owner : in_file_stat.st_uid;
358 gid_t gid = set_group_flag ? set_group : in_file_stat.st_gid;
359 if ((lchown (output_name.ds_string, uid, gid) < 0)
361 chown_error_details (output_name.ds_string, uid, gid);
368 error (0, 0, _("%s: unknown file type"), input_name.ds_string);
372 fprintf (stderr, "%s\n", output_name.ds_string);
378 fputc ('\n', stderr);
381 res = (output_bytes + io_block_size - 1) / io_block_size;
382 fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), res);
386 /* Try and create a hard link from FILE_NAME to another file
387 with the given major/minor device number and inode. If no other
388 file with the same major/minor/inode numbers is known, add this file
389 to the list of known files and associated major/minor/inode numbers
390 and return -1. If another file with the same major/minor/inode
391 numbers is found, try and create another link to it using
392 link_to_name, and return 0 for success and -1 for failure. */
395 link_to_maj_min_ino (char *file_name, int st_dev_maj, int st_dev_min,
401 /* Is the file a link to a previously copied file? */
402 link_name = find_inode_file (st_ino,
405 if (link_name == NULL)
406 add_inode (st_ino, file_name,
410 link_res = link_to_name (file_name, link_name);
414 /* Try and create a hard link from LINK_NAME to LINK_TARGET. If
415 `create_dir_flag' is set, any non-existent (parent) directories
416 needed by LINK_NAME will be created. If the link is successfully
417 created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
418 If the link can not be created and `link_flag' is set, print
419 "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
420 is created, -1 otherwise. */
423 link_to_name (char *link_name, char *link_target)
425 int res = link (link_target, link_name);
426 if (res < 0 && create_dir_flag)
428 create_all_directories (link_name);
429 res = link (link_target, link_name);
434 error (0, 0, _("%s linked to %s"),
435 link_target, link_name);
439 error (0, errno, _("cannot link %s to %s (will copy instead)"),
440 link_target, link_name);