source: trunk/essentials/app-arch/cpio/src/copypass.c

Last change on this file was 3332, checked in by bird, 18 years ago

cpio 2.7

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