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