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 */
|
---|
36 | static void
|
---|
37 | set_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 |
|
---|
48 | void
|
---|
49 | process_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 |
|
---|
376 | int
|
---|
377 | link_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 |
|
---|
405 | int
|
---|
406 | link_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 | }
|
---|