MFC:
[dragonfly.git] / contrib / cpio / copypass.c
blob9c27ac596348c2f0748af7610f6dcbfd7f6edfb2
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)
7 any later version.
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. */
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include "filetypes.h"
22 #include "system.h"
23 #include "cpiohdr.h"
24 #include "dstring.h"
25 #include "extern.h"
27 #ifndef HAVE_LCHOWN
28 #define lchown chown
29 #endif
31 /* Copy files listed on the standard input into directory `directory_name'.
32 If `link_flag', link instead of copying. */
34 void
35 process_copy_pass ()
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. */
48 #ifdef HPUX_CDF
49 int cdf_flag;
50 int cdf_char;
51 #endif
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 (&times, sizeof (struct utimbuf));
63 /* Copy files with names read from stdin. */
64 while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
66 int link_res = -1;
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");
72 continue;
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')))
80 continue;
82 if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
84 error (0, errno, "%s", input_name.ds_string);
85 continue;
88 /* Make the name of the new file. */
89 for (slash = input_name.ds_string; *slash == '/'; ++slash)
91 #ifdef HPUX_CDF
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);
97 #endif
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. */
109 existing_dir = TRUE;
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))
131 #ifndef __MSDOS__
132 /* Can the current file be linked to a another file?
133 Set link_name to the original file name. */
134 if (link_flag)
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);
145 #endif
147 /* If the file was not linked, copy contents of file. */
148 if (link_res < 0)
150 in_file_des = open (input_name.ds_string,
151 O_RDONLY | O_BINARY, 0);
152 if (in_file_des < 0)
154 error (0, errno, "%s", input_name.ds_string);
155 continue;
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);
168 close (in_file_des);
169 continue;
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. */
180 if (!no_chown_flag)
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)
184 && errno != EPERM)
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);
189 if (reset_time_flag)
191 times.actime = in_file_stat.st_atime;
192 times.modtime = in_file_stat.st_mtime;
193 if (utime (input_name.ds_string, &times) < 0)
194 error (0, errno, "%s", input_name.ds_string);
195 if (utime (output_name.ds_string, &times) < 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, &times) < 0)
202 error (0, errno, "%s", output_name.ds_string);
206 else if (S_ISDIR (in_file_stat.st_mode))
208 #ifdef HPUX_CDF
209 cdf_flag = 0;
210 #endif
211 if (!existing_dir)
213 #ifdef HPUX_CDF
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';
223 cdf_flag = 1;
225 #endif
226 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
229 else
230 res = 0;
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);
236 if (res < 0)
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);
248 continue;
251 if (!no_chown_flag)
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)
255 && errno != EPERM)
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);
260 #ifdef HPUX_CDF
261 if (cdf_flag)
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] = '+';
265 #endif
266 if (retain_time_flag)
268 times.actime = times.modtime = in_file_stat.st_mtime;
269 if (utime (output_name.ds_string, &times) < 0)
270 error (0, errno, "%s", output_name.ds_string);
273 #ifndef __MSDOS__
274 else if (S_ISCHR (in_file_stat.st_mode) ||
275 S_ISBLK (in_file_stat.st_mode) ||
276 #ifdef S_ISFIFO
277 S_ISFIFO (in_file_stat.st_mode) ||
278 #endif
279 #ifdef S_ISSOCK
280 S_ISSOCK (in_file_stat.st_mode) ||
281 #endif
284 /* Can the current file be linked to a another file?
285 Set link_name to the original file name. */
286 if (link_flag)
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);
296 if (link_res < 0)
298 #ifdef S_ISFIFO
299 if (S_ISFIFO (in_file_stat.st_mode))
300 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
301 else
302 #endif
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);
308 #ifdef S_ISFIFO
309 if (S_ISFIFO (in_file_stat.st_mode))
310 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
311 else
312 #endif
313 res = mknod (output_name.ds_string, in_file_stat.st_mode,
314 in_file_stat.st_rdev);
316 if (res < 0)
318 error (0, errno, "%s", output_name.ds_string);
319 continue;
321 if (!no_chown_flag)
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)
325 && errno != EPERM)
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, &times) < 0)
334 error (0, errno, "%s", output_name.ds_string);
338 #endif
340 #ifdef S_ISLNK
341 else if (S_ISLNK (in_file_stat.st_mode))
343 char *link_name;
344 int link_size;
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);
349 if (link_size < 0)
351 error (0, errno, "%s", input_name.ds_string);
352 free (link_name);
353 continue;
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);
365 if (res < 0)
367 error (0, errno, "%s", output_name.ds_string);
368 free (link_name);
369 continue;
372 /* Set the attributes of the new link. */
373 if (!no_chown_flag)
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)
377 && errno != EPERM)
378 error (0, errno, "%s", output_name.ds_string);
379 free (link_name);
381 #endif
382 else
384 error (0, 0, "%s: unknown file type", input_name.ds_string);
387 if (verbose_flag)
388 fprintf (stderr, "%s\n", output_name.ds_string);
389 if (dot_flag)
390 fputc ('.', stderr);
393 if (dot_flag)
394 fputc ('\n', stderr);
395 if (!quiet_flag)
397 res = (output_bytes + io_block_size - 1) / io_block_size;
398 if (res == 1)
399 fprintf (stderr, "1 block\n");
400 else
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)
415 char *file_name;
416 int st_dev_maj;
417 int st_dev_min;
418 int st_ino;
420 int link_res;
421 char *link_name;
422 link_res = -1;
423 #ifndef __MSDOS__
424 /* Is the file a link to a previously copied file? */
425 link_name = find_inode_file (st_ino,
426 st_dev_maj,
427 st_dev_min);
428 if (link_name == NULL)
429 add_inode (st_ino, file_name,
430 st_dev_maj,
431 st_dev_min);
432 else
433 link_res = link_to_name (file_name, link_name);
434 #endif
435 return link_res;
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)
448 char *link_name;
449 char *link_target;
451 int res;
452 #ifdef __MSDOS__
453 res = -1;
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);
461 if (res == 0)
463 if (verbose_flag)
464 error (0, 0, "%s linked to %s",
465 link_target, link_name);
467 else if (link_flag)
469 error (0, errno, "cannot link %s to %s",
470 link_target, link_name);
472 #endif /* not __MSDOS__ */
473 return res;