]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/cpio/src/copypass.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / cpio / src / copypass.c
1 /* $FreeBSD$ */
2
3 /* copypass.c - cpio copy pass sub-function.
4    Copyright (C) 1990, 1991, 1992, 2001, 2003, 2004 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include <system.h>
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include "filetypes.h"
26 #include "cpiohdr.h"
27 #include "dstring.h"
28 #include "extern.h"
29
30 #ifndef HAVE_LCHOWN
31 #define lchown chown
32 #endif
33
34 /* Copy files listed on the standard input into directory `directory_name'.
35    If `link_flag', link instead of copying.  */
36
37 void
38 process_copy_pass ()
39 {
40   dynamic_string input_name;    /* Name of file from stdin.  */
41   dynamic_string output_name;   /* Name of new file.  */
42   int dirname_len;              /* Length of `directory_name'.  */
43   int res;                      /* Result of functions.  */
44   char *slash;                  /* For moving past slashes in input name.  */
45   struct utimbuf times;         /* For resetting file times after copy.  */
46   struct stat in_file_stat;     /* Stat record for input file.  */
47   struct stat out_file_stat;    /* Stat record for output file.  */
48   int in_file_des;              /* Input file descriptor.  */
49   int out_file_des;             /* Output file descriptor.  */
50   int existing_dir;             /* True if file is a dir & already exists.  */
51 #ifdef HPUX_CDF
52   int cdf_flag;
53   int cdf_char;
54 #endif
55
56   /* Initialize the copy pass.  */
57   dirname_len = strlen (directory_name);
58   ds_init (&input_name, 128);
59   ds_init (&output_name, dirname_len + 2);
60   strcpy (output_name.ds_string, directory_name);
61   output_name.ds_string[dirname_len] = '/';
62   output_is_seekable = true;
63   /* Initialize this in case it has members we don't know to set.  */
64   bzero (&times, sizeof (struct utimbuf));
65
66   /* Copy files with names read from stdin.  */
67   while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
68     {
69       int link_res = -1;
70
71       /* Check for blank line and ignore it if found.  */
72       if (input_name.ds_string[0] == '\0')
73         {
74           error (0, 0, _("blank line ignored"));
75           continue;
76         }
77
78       /* Check for current directory and ignore it if found.  */
79       if (input_name.ds_string[0] == '.'
80           && (input_name.ds_string[1] == '\0'
81               || (input_name.ds_string[1] == '/'
82                   && input_name.ds_string[2] == '\0')))
83         continue;
84
85       if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
86         {
87           error (0, errno, "%s", input_name.ds_string);
88           continue;
89         }
90
91       /* Make the name of the new file.  */
92       for (slash = input_name.ds_string; *slash == '/'; ++slash)
93         ;
94 #ifdef HPUX_CDF
95       /* For CDF's we add a 2nd `/' after all "hidden" directories.
96          This kind of a kludge, but it's what we do when creating
97          archives, and it's easier to do this than to separately
98          keep track of which directories in a path are "hidden".  */
99       slash = add_cdf_double_slashes (slash);
100 #endif
101       ds_resize (&output_name, dirname_len + strlen (slash) + 2);
102       strcpy (output_name.ds_string + dirname_len + 1, slash);
103
104       existing_dir = false;
105       if (lstat (output_name.ds_string, &out_file_stat) == 0)
106         {
107           if (S_ISDIR (out_file_stat.st_mode)
108               && S_ISDIR (in_file_stat.st_mode))
109             {
110               /* If there is already a directory there that
111                  we are trying to create, don't complain about it.  */
112               existing_dir = true;
113             }
114           else if (!unconditional_flag
115                    && in_file_stat.st_mtime <= out_file_stat.st_mtime)
116             {
117               error (0, 0, _("%s not created: newer or same age version exists"),
118                      output_name.ds_string);
119               continue;         /* Go to the next file.  */
120             }
121           else if (S_ISDIR (out_file_stat.st_mode)
122                         ? rmdir (output_name.ds_string)
123                         : unlink (output_name.ds_string))
124             {
125               error (0, errno, _("cannot remove current %s"),
126                      output_name.ds_string);
127               continue;         /* Go to the next file.  */
128             }
129         }
130
131       /* Do the real copy or link.  */
132       if (S_ISREG (in_file_stat.st_mode))
133         {
134           /* Can the current file be linked to a another file?
135              Set link_name to the original file name.  */
136           if (link_flag)
137             /* User said to link it if possible.  Try and link to
138                the original copy.  If that fails we'll still try
139                and link to a copy we've already made.  */
140             link_res = link_to_name (output_name.ds_string, 
141                                      input_name.ds_string);
142           if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
143             link_res = link_to_maj_min_ino (output_name.ds_string, 
144                                 major (in_file_stat.st_dev), 
145                                 minor (in_file_stat.st_dev), 
146                                 in_file_stat.st_ino);
147
148           /* If the file was not linked, copy contents of file.  */
149           if (link_res < 0)
150             {
151               in_file_des = open (input_name.ds_string,
152                                   O_RDONLY | O_BINARY, 0);
153               if (in_file_des < 0)
154                 {
155                   error (0, errno, "%s", input_name.ds_string);
156                   continue;
157                 }
158               out_file_des = open (output_name.ds_string,
159                                    O_CREAT | O_WRONLY | O_BINARY, 0600);
160               if (out_file_des < 0 && create_dir_flag)
161                 {
162                   create_all_directories (output_name.ds_string);
163                   out_file_des = open (output_name.ds_string,
164                                        O_CREAT | O_WRONLY | O_BINARY, 0600);
165                 }
166               if (out_file_des < 0)
167                 {
168                   error (0, errno, "%s", output_name.ds_string);
169                   close (in_file_des);
170                   continue;
171                 }
172
173               copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string);
174               disk_empty_output_buffer (out_file_des);
175               /* Debian hack to fix a bug in the --sparse option.
176                  This bug has been reported to
177                  "bug-gnu-utils@prep.ai.mit.edu".  (96/7/10) -BEM */
178               if (delayed_seek_count > 0)
179                 {
180                   lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
181                   write (out_file_des, "", 1);
182                   delayed_seek_count = 0;
183                 }
184               if (close (in_file_des) < 0)
185                 error (0, errno, "%s", input_name.ds_string);
186               /*
187                *  Avoid race condition.
188                *  Set chown and chmod before closing the file desc.
189                *  pvrabec@redhat.com
190                */
191               /* Set the attributes of the new file.  */
192               if (!no_chown_flag)
193                 if ((fchown (out_file_des,
194                             set_owner_flag ? set_owner : in_file_stat.st_uid,
195                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
196                     && errno != EPERM)
197                   error (0, errno, "%s", output_name.ds_string);
198               /* chown may have turned off some permissions we wanted. */
199               if (fchmod (out_file_des, in_file_stat.st_mode) < 0)
200                 error (0, errno, "%s", output_name.ds_string);
201                 
202               if (close (out_file_des) < 0)
203                 error (0, errno, "%s", output_name.ds_string);
204
205               if (reset_time_flag)
206                 {
207                   times.actime = in_file_stat.st_atime;
208                   times.modtime = in_file_stat.st_mtime;
209                   /* Debian hack: Silently ignore EROFS because
210                      reading the file won't have upset its timestamp
211                      if it's on a read-only filesystem.  This has been
212                      submitted as a suggestion to
213                      "bug-gnu-utils@prep.ai.mit.edu".  -BEM */
214                   if (utime (input_name.ds_string, &times) < 0
215                       && errno != EROFS)
216                     error (0, errno, "%s", input_name.ds_string);
217                   if (utime (output_name.ds_string, &times) < 0
218                       && errno != EROFS)
219                     error (0, errno, "%s", output_name.ds_string);
220                 }
221               if (retain_time_flag)
222                 {
223                   times.actime = times.modtime = in_file_stat.st_mtime;
224                   if (utime (output_name.ds_string, &times) < 0)
225                     error (0, errno, "%s", output_name.ds_string);
226                 }
227               warn_if_file_changed(input_name.ds_string, in_file_stat.st_size,
228                                    in_file_stat.st_mtime);
229             }
230         }
231       else if (S_ISDIR (in_file_stat.st_mode))
232         {
233 #ifdef HPUX_CDF
234           cdf_flag = 0;
235 #endif
236           if (!existing_dir)
237             {
238 #ifdef HPUX_CDF
239               /* If the directory name ends in a + and is SUID,
240                  then it is a CDF.  Strip the trailing + from the name
241                  before creating it.  */
242               cdf_char = strlen (output_name.ds_string) - 1;
243               if ( (cdf_char > 0) &&
244                    (in_file_stat.st_mode & 04000) &&
245                    (output_name.ds_string [cdf_char] == '+') )
246                 {
247                   output_name.ds_string [cdf_char] = '\0';
248                   cdf_flag = 1;
249                 }
250 #endif
251               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
252
253             }
254           else
255             res = 0;
256           if (res < 0 && create_dir_flag)
257             {
258               create_all_directories (output_name.ds_string);
259               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
260             }
261           if (res < 0)
262             {
263               /* In some odd cases where the output_name includes `.',
264                  the directory may have actually been created by
265                  create_all_directories(), so the mkdir will fail
266                  because the directory exists.  If that's the case,
267                  don't complain about it.  */
268               if ( (errno != EEXIST) ||
269                    (lstat (output_name.ds_string, &out_file_stat) != 0) ||
270                    !(S_ISDIR (out_file_stat.st_mode) ) )
271                 {
272                   error (0, errno, "%s", output_name.ds_string);
273                   continue;
274                 }
275             }
276           if (!no_chown_flag)
277             if ((chown (output_name.ds_string,
278                         set_owner_flag ? set_owner : in_file_stat.st_uid,
279                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
280                 && errno != EPERM)
281               error (0, errno, "%s", output_name.ds_string);
282           /* chown may have turned off some permissions we wanted. */
283           if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
284             error (0, errno, "%s", output_name.ds_string);
285 #ifdef HPUX_CDF
286           if (cdf_flag)
287             /* Once we "hide" the directory with the chmod(),
288                we have to refer to it using name+ isntead of name.  */
289             output_name.ds_string [cdf_char] = '+';
290 #endif
291           if (retain_time_flag)
292             {
293               times.actime = times.modtime = in_file_stat.st_mtime;
294               if (utime (output_name.ds_string, &times) < 0)
295                 error (0, errno, "%s", output_name.ds_string);
296             }
297         }
298       else if (S_ISCHR (in_file_stat.st_mode) ||
299                S_ISBLK (in_file_stat.st_mode) ||
300 #ifdef S_ISFIFO
301                S_ISFIFO (in_file_stat.st_mode) ||
302 #endif
303 #ifdef S_ISSOCK
304                S_ISSOCK (in_file_stat.st_mode) ||
305 #endif
306                0)
307         {
308           /* Can the current file be linked to a another file?
309              Set link_name to the original file name.  */
310           if (link_flag)
311             /* User said to link it if possible.  */
312             link_res = link_to_name (output_name.ds_string, 
313                                      input_name.ds_string);
314           if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
315             link_res = link_to_maj_min_ino (output_name.ds_string, 
316                         major (in_file_stat.st_dev),
317                         minor (in_file_stat.st_dev),
318                         in_file_stat.st_ino);
319
320           if (link_res < 0)
321             {
322 #ifdef S_ISFIFO
323               if (S_ISFIFO (in_file_stat.st_mode))
324                 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
325               else
326 #endif
327                 res = mknod (output_name.ds_string, in_file_stat.st_mode,
328                              in_file_stat.st_rdev);
329               if (res < 0 && create_dir_flag)
330                 {
331                   create_all_directories (output_name.ds_string);
332 #ifdef S_ISFIFO
333                   if (S_ISFIFO (in_file_stat.st_mode))
334                     res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
335                   else
336 #endif
337                     res = mknod (output_name.ds_string, in_file_stat.st_mode,
338                                  in_file_stat.st_rdev);
339                 }
340               if (res < 0)
341                 {
342                   error (0, errno, "%s", output_name.ds_string);
343                   continue;
344                 }
345               if (!no_chown_flag)
346                 if ((chown (output_name.ds_string,
347                             set_owner_flag ? set_owner : in_file_stat.st_uid,
348                           set_group_flag ? set_group : in_file_stat.st_gid) < 0)
349                     && errno != EPERM)
350                   error (0, errno, "%s", output_name.ds_string);
351               /* chown may have turned off some permissions we wanted. */
352               if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
353                 error (0, errno, "%s", output_name.ds_string);
354               if (retain_time_flag)
355                 {
356                   times.actime = times.modtime = in_file_stat.st_mtime;
357                   if (utime (output_name.ds_string, &times) < 0)
358                     error (0, errno, "%s", output_name.ds_string);
359                 }
360             }
361         }
362
363 #ifdef S_ISLNK
364       else if (S_ISLNK (in_file_stat.st_mode))
365         {
366           char *link_name;
367           int link_size;
368           link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
369
370           link_size = readlink (input_name.ds_string, link_name,
371                                 in_file_stat.st_size);
372           if (link_size < 0)
373             {
374               error (0, errno, "%s", input_name.ds_string);
375               free (link_name);
376               continue;
377             }
378           link_name[link_size] = '\0';
379
380           res = UMASKED_SYMLINK (link_name, output_name.ds_string,
381                                  in_file_stat.st_mode);
382           if (res < 0 && create_dir_flag)
383             {
384               create_all_directories (output_name.ds_string);
385               res = UMASKED_SYMLINK (link_name, output_name.ds_string,
386                                      in_file_stat.st_mode);
387             }
388           if (res < 0)
389             {
390               error (0, errno, "%s", output_name.ds_string);
391               free (link_name);
392               continue;
393             }
394
395           /* Set the attributes of the new link.  */
396           if (!no_chown_flag)
397             if ((lchown (output_name.ds_string,
398                          set_owner_flag ? set_owner : in_file_stat.st_uid,
399                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
400                 && errno != EPERM)
401               error (0, errno, "%s", output_name.ds_string);
402           free (link_name);
403         }
404 #endif
405       else
406         {
407           error (0, 0, _("%s: unknown file type"), input_name.ds_string);
408         }
409
410       if (verbose_flag)
411         fprintf (stderr, "%s\n", output_name.ds_string);
412       if (dot_flag)
413         fputc ('.', stderr);
414     }
415
416   if (dot_flag)
417     fputc ('\n', stderr);
418   if (!quiet_flag)
419     {
420       res = (output_bytes + io_block_size - 1) / io_block_size;
421       fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), res);
422     }
423 }
424 \f
425 /* Try and create a hard link from FILE_NAME to another file 
426    with the given major/minor device number and inode.  If no other
427    file with the same major/minor/inode numbers is known, add this file
428    to the list of known files and associated major/minor/inode numbers
429    and return -1.  If another file with the same major/minor/inode
430    numbers is found, try and create another link to it using
431    link_to_name, and return 0 for success and -1 for failure.  */
432
433 int
434 link_to_maj_min_ino (char *file_name, int st_dev_maj, int st_dev_min,
435                      int st_ino)
436 {
437   int   link_res;
438   char *link_name;
439   link_res = -1;
440   /* Is the file a link to a previously copied file?  */
441   link_name = find_inode_file (st_ino,
442                                st_dev_maj,
443                                st_dev_min);
444   if (link_name == NULL)
445     add_inode (st_ino, file_name,
446                st_dev_maj,
447                st_dev_min);
448   else
449     link_res = link_to_name (file_name, link_name);
450   return link_res;
451 }
452 \f
453 /* Try and create a hard link from LINK_NAME to LINK_TARGET.  If
454    `create_dir_flag' is set, any non-existent (parent) directories 
455    needed by LINK_NAME will be created.  If the link is successfully
456    created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
457    If the link can not be created and `link_flag' is set, print
458    "cannot link LINK_TARGET to LINK_NAME\n".  Return 0 if the link
459    is created, -1 otherwise.  */
460
461 int
462 link_to_name (char *link_name, char *link_target)
463 {
464   int res = link (link_target, link_name);
465   if (res < 0 && create_dir_flag)
466     {
467       create_all_directories (link_name);
468       res = link (link_target, link_name);
469     }
470   if (res == 0)
471     {
472       if (verbose_flag)
473         error (0, 0, _("%s linked to %s"),
474                link_target, link_name);
475     }
476   else if (link_flag)
477     {
478       error (0, errno, _("cannot link %s to %s (will copy instead)"),
479              link_target, link_name);
480     }
481   return res;
482 }