1 /* copypass.c - cpio copy pass sub-function.
2 Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19 #include <sys/types.h>
21 #include "filetypes.h"
31 /* Copy files listed on the standard input into directory `directory_name'.
32 If `link_flag', link instead of copying. */
37 dynamic_string input_name; /* Name of file from stdin. */
38 dynamic_string output_name; /* Name of new file. */
39 int dirname_len; /* Length of `directory_name'. */
40 int res; /* Result of functions. */
41 char *slash; /* For moving past slashes in input name. */
42 struct utimbuf times; /* For resetting file times after copy. */
43 struct stat in_file_stat; /* Stat record for input file. */
44 struct stat out_file_stat; /* Stat record for output file. */
45 int in_file_des; /* Input file descriptor. */
46 int out_file_des; /* Output file descriptor. */
47 int existing_dir; /* True if file is a dir & already exists. */
53 /* Initialize the copy pass. */
54 dirname_len = strlen (directory_name);
55 ds_init (&input_name, 128);
56 ds_init (&output_name, dirname_len + 2);
57 strcpy (output_name.ds_string, directory_name);
58 output_name.ds_string[dirname_len] = '/';
59 output_is_seekable = TRUE;
60 /* Initialize this in case it has members we don't know to set. */
61 bzero (×, sizeof (struct utimbuf));
63 /* Copy files with names read from stdin. */
64 while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
68 /* Check for blank line and ignore it if found. */
69 if (input_name.ds_string[0] == '\0')
71 error (0, 0, "blank line ignored");
75 /* Check for current directory and ignore it if found. */
76 if (input_name.ds_string[0] == '.'
77 && (input_name.ds_string[1] == '\0'
78 || (input_name.ds_string[1] == '/'
79 && input_name.ds_string[2] == '\0')))
82 if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
84 error (0, errno, "%s", input_name.ds_string);
88 /* Make the name of the new file. */
89 for (slash = input_name.ds_string; *slash == '/'; ++slash)
92 /* For CDF's we add a 2nd `/' after all "hidden" directories.
93 This kind of a kludge, but it's what we do when creating
94 archives, and it's easier to do this than to separately
95 keep track of which directories in a path are "hidden". */
96 slash = add_cdf_double_slashes (slash);
98 ds_resize (&output_name, dirname_len + strlen (slash) + 2);
99 strcpy (output_name.ds_string + dirname_len + 1, slash);
101 existing_dir = FALSE;
102 if (lstat (output_name.ds_string, &out_file_stat) == 0)
104 if (S_ISDIR (out_file_stat.st_mode)
105 && S_ISDIR (in_file_stat.st_mode))
107 /* If there is already a directory there that
108 we are trying to create, don't complain about it. */
111 else if (!unconditional_flag
112 && in_file_stat.st_mtime <= out_file_stat.st_mtime)
114 error (0, 0, "%s not created: newer or same age version exists",
115 output_name.ds_string);
116 continue; /* Go to the next file. */
118 else if (S_ISDIR (out_file_stat.st_mode)
119 ? rmdir (output_name.ds_string)
120 : unlink (output_name.ds_string))
122 error (0, errno, "cannot remove current %s",
123 output_name.ds_string);
124 continue; /* Go to the next file. */
128 /* Do the real copy or link. */
129 if (S_ISREG (in_file_stat.st_mode))
132 /* Can the current file be linked to a another file?
133 Set link_name to the original file name. */
135 /* User said to link it if possible. Try and link to
136 the original copy. If that fails we'll still try
137 and link to a copy we've already made. */
138 link_res = link_to_name (output_name.ds_string,
139 input_name.ds_string);
140 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
141 link_res = link_to_maj_min_ino (output_name.ds_string,
142 major (in_file_stat.st_dev),
143 minor (in_file_stat.st_dev),
144 in_file_stat.st_ino);
147 /* If the file was not linked, copy contents of file. */
150 in_file_des = open (input_name.ds_string,
151 O_RDONLY | O_BINARY, 0);
154 error (0, errno, "%s", input_name.ds_string);
157 out_file_des = open (output_name.ds_string,
158 O_CREAT | O_WRONLY | O_BINARY, 0600);
159 if (out_file_des < 0 && create_dir_flag)
161 create_all_directories (output_name.ds_string);
162 out_file_des = open (output_name.ds_string,
163 O_CREAT | O_WRONLY | O_BINARY, 0600);
165 if (out_file_des < 0)
167 error (0, errno, "%s", output_name.ds_string);
172 copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string);
173 disk_empty_output_buffer (out_file_des);
174 if (close (in_file_des) < 0)
175 error (0, errno, "%s", input_name.ds_string);
176 if (close (out_file_des) < 0)
177 error (0, errno, "%s", output_name.ds_string);
179 /* Set the attributes of the new file. */
181 if ((chown (output_name.ds_string,
182 set_owner_flag ? set_owner : in_file_stat.st_uid,
183 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
185 error (0, errno, "%s", output_name.ds_string);
186 /* chown may have turned off some permissions we wanted. */
187 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
188 error (0, errno, "%s", output_name.ds_string);
191 times.actime = in_file_stat.st_atime;
192 times.modtime = in_file_stat.st_mtime;
193 if (utime (input_name.ds_string, ×) < 0)
194 error (0, errno, "%s", input_name.ds_string);
195 if (utime (output_name.ds_string, ×) < 0)
196 error (0, errno, "%s", output_name.ds_string);
198 if (retain_time_flag)
200 times.actime = times.modtime = in_file_stat.st_mtime;
201 if (utime (output_name.ds_string, ×) < 0)
202 error (0, errno, "%s", output_name.ds_string);
206 else if (S_ISDIR (in_file_stat.st_mode))
214 /* If the directory name ends in a + and is SUID,
215 then it is a CDF. Strip the trailing + from the name
216 before creating it. */
217 cdf_char = strlen (output_name.ds_string) - 1;
218 if ( (cdf_char > 0) &&
219 (in_file_stat.st_mode & 04000) &&
220 (output_name.ds_string [cdf_char] == '+') )
222 output_name.ds_string [cdf_char] = '\0';
226 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
231 if (res < 0 && create_dir_flag)
233 create_all_directories (output_name.ds_string);
234 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
238 /* In some odd cases where the output_name includes `.',
239 the directory may have actually been created by
240 create_all_directories(), so the mkdir will fail
241 because the directory exists. If that's the case,
242 don't complain about it. */
243 if ( (errno != EEXIST) ||
244 (lstat (output_name.ds_string, &out_file_stat) != 0) ||
245 !(S_ISDIR (out_file_stat.st_mode) ) )
247 error (0, errno, "%s", output_name.ds_string);
252 if ((chown (output_name.ds_string,
253 set_owner_flag ? set_owner : in_file_stat.st_uid,
254 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
256 error (0, errno, "%s", output_name.ds_string);
257 /* chown may have turned off some permissions we wanted. */
258 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
259 error (0, errno, "%s", output_name.ds_string);
262 /* Once we "hide" the directory with the chmod(),
263 we have to refer to it using name+ isntead of name. */
264 output_name.ds_string [cdf_char] = '+';
266 if (retain_time_flag)
268 times.actime = times.modtime = in_file_stat.st_mtime;
269 if (utime (output_name.ds_string, ×) < 0)
270 error (0, errno, "%s", output_name.ds_string);
274 else if (S_ISCHR (in_file_stat.st_mode) ||
275 S_ISBLK (in_file_stat.st_mode) ||
277 S_ISFIFO (in_file_stat.st_mode) ||
280 S_ISSOCK (in_file_stat.st_mode) ||
284 /* Can the current file be linked to a another file?
285 Set link_name to the original file name. */
287 /* User said to link it if possible. */
288 link_res = link_to_name (output_name.ds_string,
289 input_name.ds_string);
290 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
291 link_res = link_to_maj_min_ino (output_name.ds_string,
292 major (in_file_stat.st_dev),
293 minor (in_file_stat.st_dev),
294 in_file_stat.st_ino);
299 if (S_ISFIFO (in_file_stat.st_mode))
300 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
303 res = mknod (output_name.ds_string, in_file_stat.st_mode,
304 in_file_stat.st_rdev);
305 if (res < 0 && create_dir_flag)
307 create_all_directories (output_name.ds_string);
309 if (S_ISFIFO (in_file_stat.st_mode))
310 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
313 res = mknod (output_name.ds_string, in_file_stat.st_mode,
314 in_file_stat.st_rdev);
318 error (0, errno, "%s", output_name.ds_string);
322 if ((chown (output_name.ds_string,
323 set_owner_flag ? set_owner : in_file_stat.st_uid,
324 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
326 error (0, errno, "%s", output_name.ds_string);
327 /* chown may have turned off some permissions we wanted. */
328 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
329 error (0, errno, "%s", output_name.ds_string);
330 if (retain_time_flag)
332 times.actime = times.modtime = in_file_stat.st_mtime;
333 if (utime (output_name.ds_string, ×) < 0)
334 error (0, errno, "%s", output_name.ds_string);
341 else if (S_ISLNK (in_file_stat.st_mode))
345 link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
347 link_size = readlink (input_name.ds_string, link_name,
348 in_file_stat.st_size);
351 error (0, errno, "%s", input_name.ds_string);
355 link_name[link_size] = '\0';
357 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
358 in_file_stat.st_mode);
359 if (res < 0 && create_dir_flag)
361 create_all_directories (output_name.ds_string);
362 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
363 in_file_stat.st_mode);
367 error (0, errno, "%s", output_name.ds_string);
372 /* Set the attributes of the new link. */
374 if ((lchown (output_name.ds_string,
375 set_owner_flag ? set_owner : in_file_stat.st_uid,
376 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
378 error (0, errno, "%s", output_name.ds_string);
384 error (0, 0, "%s: unknown file type", input_name.ds_string);
388 fprintf (stderr, "%s\n", output_name.ds_string);
394 fputc ('\n', stderr);
397 res = (output_bytes + io_block_size - 1) / io_block_size;
399 fprintf (stderr, "1 block\n");
401 fprintf (stderr, "%d blocks\n", res);
405 /* Try and create a hard link from FILE_NAME to another file
406 with the given major/minor device number and inode. If no other
407 file with the same major/minor/inode numbers is known, add this file
408 to the list of known files and associated major/minor/inode numbers
409 and return -1. If another file with the same major/minor/inode
410 numbers is found, try and create another link to it using
411 link_to_name, and return 0 for success and -1 for failure. */
414 link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino)
424 /* Is the file a link to a previously copied file? */
425 link_name = find_inode_file (st_ino,
428 if (link_name == NULL)
429 add_inode (st_ino, file_name,
433 link_res = link_to_name (file_name, link_name);
438 /* Try and create a hard link from LINK_NAME to LINK_TARGET. If
439 `create_dir_flag' is set, any non-existent (parent) directories
440 needed by LINK_NAME will be created. If the link is successfully
441 created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
442 If the link can not be created and `link_flag' is set, print
443 "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
444 is created, -1 otherwise. */
447 link_to_name (link_name, link_target)
454 #else /* not __MSDOS__ */
455 res = link (link_target, link_name);
456 if (res < 0 && create_dir_flag)
458 create_all_directories (link_name);
459 res = link (link_target, link_name);
464 error (0, 0, "%s linked to %s",
465 link_target, link_name);
469 error (0, errno, "cannot link %s to %s",
470 link_target, link_name);
472 #endif /* not __MSDOS__ */