Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / cpio / copypass.c
1 /* copypass.c - cpio copy pass sub-function.
2    Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
3
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.
8
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.
13
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.  */
17
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"
26
27 #ifndef HAVE_LCHOWN
28 #define lchown chown
29 #endif
30
31 /* Copy files listed on the standard input into directory `directory_name'.
32    If `link_flag', link instead of copying.  */
33
34 void
35 process_copy_pass ()
36 {
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
52
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));
62
63   /* Copy files with names read from stdin.  */
64   while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
65     {
66       int link_res = -1;
67
68       /* Check for blank line and ignore it if found.  */
69       if (input_name.ds_string[0] == '\0')
70         {
71           error (0, 0, "blank line ignored");
72           continue;
73         }
74
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;
81
82       if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
83         {
84           error (0, errno, "%s", input_name.ds_string);
85           continue;
86         }
87
88       /* Make the name of the new file.  */
89       for (slash = input_name.ds_string; *slash == '/'; ++slash)
90         ;
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);
100
101       existing_dir = FALSE;
102       if (lstat (output_name.ds_string, &out_file_stat) == 0)
103         {
104           if (S_ISDIR (out_file_stat.st_mode)
105               && S_ISDIR (in_file_stat.st_mode))
106             {
107               /* If there is already a directory there that
108                  we are trying to create, don't complain about it.  */
109               existing_dir = TRUE;
110             }
111           else if (!unconditional_flag
112                    && in_file_stat.st_mtime <= out_file_stat.st_mtime)
113             {
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.  */
117             }
118           else if (S_ISDIR (out_file_stat.st_mode)
119                         ? rmdir (output_name.ds_string)
120                         : unlink (output_name.ds_string))
121             {
122               error (0, errno, "cannot remove current %s",
123                      output_name.ds_string);
124               continue;         /* Go to the next file.  */
125             }
126         }
127
128       /* Do the real copy or link.  */
129       if (S_ISREG (in_file_stat.st_mode))
130         {
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
146
147           /* If the file was not linked, copy contents of file.  */
148           if (link_res < 0)
149             {
150               in_file_des = open (input_name.ds_string,
151                                   O_RDONLY | O_BINARY, 0);
152               if (in_file_des < 0)
153                 {
154                   error (0, errno, "%s", input_name.ds_string);
155                   continue;
156                 }
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)
160                 {
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);
164                 }
165               if (out_file_des < 0)
166                 {
167                   error (0, errno, "%s", output_name.ds_string);
168                   close (in_file_des);
169                   continue;
170                 }
171
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);
178
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)
190                 {
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);
197                 }
198               if (retain_time_flag)
199                 {
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);
203                 }
204             }
205         }
206       else if (S_ISDIR (in_file_stat.st_mode))
207         {
208 #ifdef HPUX_CDF
209           cdf_flag = 0;
210 #endif
211           if (!existing_dir)
212             {
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] == '+') )
221                 {
222                   output_name.ds_string [cdf_char] = '\0';
223                   cdf_flag = 1;
224                 }
225 #endif
226               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
227
228             }
229           else
230             res = 0;
231           if (res < 0 && create_dir_flag)
232             {
233               create_all_directories (output_name.ds_string);
234               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
235             }
236           if (res < 0)
237             {
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) ) )
246                 {
247                   error (0, errno, "%s", output_name.ds_string);
248                   continue;
249                 }
250             }
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)
267             {
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);
271             }
272         }
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
282                0)
283         {
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);
295
296           if (link_res < 0)
297             {
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)
306                 {
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);
315                 }
316               if (res < 0)
317                 {
318                   error (0, errno, "%s", output_name.ds_string);
319                   continue;
320                 }
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)
331                 {
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);
335                 }
336             }
337         }
338 #endif
339
340 #ifdef S_ISLNK
341       else if (S_ISLNK (in_file_stat.st_mode))
342         {
343           char *link_name;
344           int link_size;
345           link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
346
347           link_size = readlink (input_name.ds_string, link_name,
348                                 in_file_stat.st_size);
349           if (link_size < 0)
350             {
351               error (0, errno, "%s", input_name.ds_string);
352               free (link_name);
353               continue;
354             }
355           link_name[link_size] = '\0';
356
357           res = UMASKED_SYMLINK (link_name, output_name.ds_string,
358                                  in_file_stat.st_mode);
359           if (res < 0 && create_dir_flag)
360             {
361               create_all_directories (output_name.ds_string);
362               res = UMASKED_SYMLINK (link_name, output_name.ds_string,
363                                      in_file_stat.st_mode);
364             }
365           if (res < 0)
366             {
367               error (0, errno, "%s", output_name.ds_string);
368               free (link_name);
369               continue;
370             }
371
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);
380         }
381 #endif
382       else
383         {
384           error (0, 0, "%s: unknown file type", input_name.ds_string);
385         }
386
387       if (verbose_flag)
388         fprintf (stderr, "%s\n", output_name.ds_string);
389       if (dot_flag)
390         fputc ('.', stderr);
391     }
392
393   if (dot_flag)
394     fputc ('\n', stderr);
395   if (!quiet_flag)
396     {
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);
402     }
403 }
404 \f
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.  */
412
413 int
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;
419 {
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;
436 }
437 \f
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.  */
445
446 int
447 link_to_name (link_name, link_target)
448   char *link_name;
449   char *link_target;
450 {
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)
457     {
458       create_all_directories (link_name);
459       res = link (link_target, link_name);
460     }
461   if (res == 0)
462     {
463       if (verbose_flag)
464         error (0, 0, "%s linked to %s",
465                link_target, link_name);
466     }
467   else if (link_flag)
468     {
469       error (0, errno, "cannot link %s to %s",
470              link_target, link_name);
471     }
472 #endif /* not __MSDOS__ */
473   return res;
474 }