]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cpio/src/copypass.c
Merge from old copyin.c rev 1.5, copypass.c rev 1.2: Create fifos using
[FreeBSD/FreeBSD.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               if (close (out_file_des) < 0)
187                 error (0, errno, "%s", output_name.ds_string);
188
189               /* Set the attributes of the new file.  */
190               if (!no_chown_flag)
191                 if ((chown (output_name.ds_string,
192                             set_owner_flag ? set_owner : in_file_stat.st_uid,
193                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
194                     && errno != EPERM)
195                   error (0, errno, "%s", output_name.ds_string);
196               /* chown may have turned off some permissions we wanted. */
197               if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
198                 error (0, errno, "%s", output_name.ds_string);
199               if (reset_time_flag)
200                 {
201                   times.actime = in_file_stat.st_atime;
202                   times.modtime = in_file_stat.st_mtime;
203                   /* Debian hack: Silently ignore EROFS because
204                      reading the file won't have upset its timestamp
205                      if it's on a read-only filesystem.  This has been
206                      submitted as a suggestion to
207                      "bug-gnu-utils@prep.ai.mit.edu".  -BEM */
208                   if (utime (input_name.ds_string, &times) < 0
209                       && errno != EROFS)
210                     error (0, errno, "%s", input_name.ds_string);
211                   if (utime (output_name.ds_string, &times) < 0
212                       && errno != EROFS)
213                     error (0, errno, "%s", output_name.ds_string);
214                 }
215               if (retain_time_flag)
216                 {
217                   times.actime = times.modtime = in_file_stat.st_mtime;
218                   if (utime (output_name.ds_string, &times) < 0)
219                     error (0, errno, "%s", output_name.ds_string);
220                 }
221               warn_if_file_changed(input_name.ds_string, in_file_stat.st_size,
222                                    in_file_stat.st_mtime);
223             }
224         }
225       else if (S_ISDIR (in_file_stat.st_mode))
226         {
227 #ifdef HPUX_CDF
228           cdf_flag = 0;
229 #endif
230           if (!existing_dir)
231             {
232 #ifdef HPUX_CDF
233               /* If the directory name ends in a + and is SUID,
234                  then it is a CDF.  Strip the trailing + from the name
235                  before creating it.  */
236               cdf_char = strlen (output_name.ds_string) - 1;
237               if ( (cdf_char > 0) &&
238                    (in_file_stat.st_mode & 04000) &&
239                    (output_name.ds_string [cdf_char] == '+') )
240                 {
241                   output_name.ds_string [cdf_char] = '\0';
242                   cdf_flag = 1;
243                 }
244 #endif
245               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
246
247             }
248           else
249             res = 0;
250           if (res < 0 && create_dir_flag)
251             {
252               create_all_directories (output_name.ds_string);
253               res = mkdir (output_name.ds_string, in_file_stat.st_mode);
254             }
255           if (res < 0)
256             {
257               /* In some odd cases where the output_name includes `.',
258                  the directory may have actually been created by
259                  create_all_directories(), so the mkdir will fail
260                  because the directory exists.  If that's the case,
261                  don't complain about it.  */
262               if ( (errno != EEXIST) ||
263                    (lstat (output_name.ds_string, &out_file_stat) != 0) ||
264                    !(S_ISDIR (out_file_stat.st_mode) ) )
265                 {
266                   error (0, errno, "%s", output_name.ds_string);
267                   continue;
268                 }
269             }
270           if (!no_chown_flag)
271             if ((chown (output_name.ds_string,
272                         set_owner_flag ? set_owner : in_file_stat.st_uid,
273                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
274                 && errno != EPERM)
275               error (0, errno, "%s", output_name.ds_string);
276           /* chown may have turned off some permissions we wanted. */
277           if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
278             error (0, errno, "%s", output_name.ds_string);
279 #ifdef HPUX_CDF
280           if (cdf_flag)
281             /* Once we "hide" the directory with the chmod(),
282                we have to refer to it using name+ isntead of name.  */
283             output_name.ds_string [cdf_char] = '+';
284 #endif
285           if (retain_time_flag)
286             {
287               times.actime = times.modtime = in_file_stat.st_mtime;
288               if (utime (output_name.ds_string, &times) < 0)
289                 error (0, errno, "%s", output_name.ds_string);
290             }
291         }
292       else if (S_ISCHR (in_file_stat.st_mode) ||
293                S_ISBLK (in_file_stat.st_mode) ||
294 #ifdef S_ISFIFO
295                S_ISFIFO (in_file_stat.st_mode) ||
296 #endif
297 #ifdef S_ISSOCK
298                S_ISSOCK (in_file_stat.st_mode) ||
299 #endif
300                0)
301         {
302           /* Can the current file be linked to a another file?
303              Set link_name to the original file name.  */
304           if (link_flag)
305             /* User said to link it if possible.  */
306             link_res = link_to_name (output_name.ds_string, 
307                                      input_name.ds_string);
308           if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
309             link_res = link_to_maj_min_ino (output_name.ds_string, 
310                         major (in_file_stat.st_dev),
311                         minor (in_file_stat.st_dev),
312                         in_file_stat.st_ino);
313
314           if (link_res < 0)
315             {
316 #ifdef S_ISFIFO
317               if (S_ISFIFO (in_file_stat.st_mode))
318                 res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
319               else
320 #endif
321                 res = mknod (output_name.ds_string, in_file_stat.st_mode,
322                              in_file_stat.st_rdev);
323               if (res < 0 && create_dir_flag)
324                 {
325                   create_all_directories (output_name.ds_string);
326 #ifdef S_ISFIFO
327                   if (S_ISFIFO (in_file_stat.st_mode))
328                     res = mkfifo (output_name.ds_string, in_file_stat.st_mode);
329                   else
330 #endif
331                     res = mknod (output_name.ds_string, in_file_stat.st_mode,
332                                  in_file_stat.st_rdev);
333                 }
334               if (res < 0)
335                 {
336                   error (0, errno, "%s", output_name.ds_string);
337                   continue;
338                 }
339               if (!no_chown_flag)
340                 if ((chown (output_name.ds_string,
341                             set_owner_flag ? set_owner : in_file_stat.st_uid,
342                           set_group_flag ? set_group : in_file_stat.st_gid) < 0)
343                     && errno != EPERM)
344                   error (0, errno, "%s", output_name.ds_string);
345               /* chown may have turned off some permissions we wanted. */
346               if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
347                 error (0, errno, "%s", output_name.ds_string);
348               if (retain_time_flag)
349                 {
350                   times.actime = times.modtime = in_file_stat.st_mtime;
351                   if (utime (output_name.ds_string, &times) < 0)
352                     error (0, errno, "%s", output_name.ds_string);
353                 }
354             }
355         }
356
357 #ifdef S_ISLNK
358       else if (S_ISLNK (in_file_stat.st_mode))
359         {
360           char *link_name;
361           int link_size;
362           link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
363
364           link_size = readlink (input_name.ds_string, link_name,
365                                 in_file_stat.st_size);
366           if (link_size < 0)
367             {
368               error (0, errno, "%s", input_name.ds_string);
369               free (link_name);
370               continue;
371             }
372           link_name[link_size] = '\0';
373
374           res = UMASKED_SYMLINK (link_name, output_name.ds_string,
375                                  in_file_stat.st_mode);
376           if (res < 0 && create_dir_flag)
377             {
378               create_all_directories (output_name.ds_string);
379               res = UMASKED_SYMLINK (link_name, output_name.ds_string,
380                                      in_file_stat.st_mode);
381             }
382           if (res < 0)
383             {
384               error (0, errno, "%s", output_name.ds_string);
385               free (link_name);
386               continue;
387             }
388
389           /* Set the attributes of the new link.  */
390           if (!no_chown_flag)
391             if ((lchown (output_name.ds_string,
392                          set_owner_flag ? set_owner : in_file_stat.st_uid,
393                       set_group_flag ? set_group : in_file_stat.st_gid) < 0)
394                 && errno != EPERM)
395               error (0, errno, "%s", output_name.ds_string);
396           free (link_name);
397         }
398 #endif
399       else
400         {
401           error (0, 0, _("%s: unknown file type"), input_name.ds_string);
402         }
403
404       if (verbose_flag)
405         fprintf (stderr, "%s\n", output_name.ds_string);
406       if (dot_flag)
407         fputc ('.', stderr);
408     }
409
410   if (dot_flag)
411     fputc ('\n', stderr);
412   if (!quiet_flag)
413     {
414       res = (output_bytes + io_block_size - 1) / io_block_size;
415       fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), res);
416     }
417 }
418 \f
419 /* Try and create a hard link from FILE_NAME to another file 
420    with the given major/minor device number and inode.  If no other
421    file with the same major/minor/inode numbers is known, add this file
422    to the list of known files and associated major/minor/inode numbers
423    and return -1.  If another file with the same major/minor/inode
424    numbers is found, try and create another link to it using
425    link_to_name, and return 0 for success and -1 for failure.  */
426
427 int
428 link_to_maj_min_ino (char *file_name, int st_dev_maj, int st_dev_min,
429                      int st_ino)
430 {
431   int   link_res;
432   char *link_name;
433   link_res = -1;
434   /* Is the file a link to a previously copied file?  */
435   link_name = find_inode_file (st_ino,
436                                st_dev_maj,
437                                st_dev_min);
438   if (link_name == NULL)
439     add_inode (st_ino, file_name,
440                st_dev_maj,
441                st_dev_min);
442   else
443     link_res = link_to_name (file_name, link_name);
444   return link_res;
445 }
446 \f
447 /* Try and create a hard link from LINK_NAME to LINK_TARGET.  If
448    `create_dir_flag' is set, any non-existent (parent) directories 
449    needed by LINK_NAME will be created.  If the link is successfully
450    created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
451    If the link can not be created and `link_flag' is set, print
452    "cannot link LINK_TARGET to LINK_NAME\n".  Return 0 if the link
453    is created, -1 otherwise.  */
454
455 int
456 link_to_name (char *link_name, char *link_target)
457 {
458   int res = link (link_target, link_name);
459   if (res < 0 && create_dir_flag)
460     {
461       create_all_directories (link_name);
462       res = link (link_target, link_name);
463     }
464   if (res == 0)
465     {
466       if (verbose_flag)
467         error (0, 0, _("%s linked to %s"),
468                link_target, link_name);
469     }
470   else if (link_flag)
471     {
472       error (0, errno, _("cannot link %s to %s"),
473              link_target, link_name);
474     }
475   return res;
476 }