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